Call the instant REST API
Every entity gets a REST API as soon as you create it. Authenticate, then list, get, create, update and delete records with curl, the CLI or MCP.
Last updated
You don't write an API with Anythink. You model your data, and the API is already there. Create an entity called orders, add a few fields, and you can list, read, create, change and delete orders over HTTP straight away. This guide takes you from a new entity to working requests. For every endpoint, parameter and status code, keep REST API reference open beside it.
How the API is generated
Each entity is a table, and each field is a typed column. When you save either one, Anythink changes the database and the API in the same step. There's nothing to deploy and no routes to write.
- Add a field and it appears in requests and responses at once.
- Every entity gets the same nine record endpoints, so what you learn for
ordersworks for every other entity. - Four permissions are created with the entity:
orders:read,orders:create,orders:updateandorders:delete. Each endpoint checks one of them. - Requests and responses are JSON, and every property name is
snake_case.
The examples on this page use an orders entity with five fields:
| Field | Type | Notes |
|---|---|---|
reference |
Small text | Required and unique. It's the entity's name field. |
total |
Decimal | |
status |
Small text | A select with the choices draft, paid and shipped. |
placed_on |
Date | |
paid |
Boolean | Defaults to false. |
To create it yourself, follow Model your data: entities, fields, relations. Every record also gets an id, created_at and updated_at, which Anythink sets for you.
Base URL and project ID
Every endpoint lives under your project:
https://api.my.anythink.cloud/org/{project_id}
Your project ID is the number after /org/ in the dashboard address. The record endpoints for an entity sit under /entities/{entity}/items, so the orders list is:
GET https://api.my.anythink.cloud/org/{project_id}/entities/orders/items
The entity name is the lower-case name from your data model, written exactly as it is there.
The curl examples below read two environment variables, so you can paste them as they are:
export ORG_ID=12345
export ANYTHINK_API_KEY=ak_your_key_here
export BASE=https://api.my.anythink.cloud/org/$ORG_ID
Authenticate
Every record endpoint needs credentials. Send one of two things:
| Credential | Header | Use it for |
|---|---|---|
| User access token | Authorization: Bearer <access_token> |
Apps where a person is signed in. Each request runs as that user. See Sign in users. |
| API key | x-api-key: ak_… |
Servers, scripts and CI with nobody signed in. See API keys. |
The two calls below do the same thing:
curl "$BASE/entities/orders/items" -H "Authorization: Bearer $ACCESS_TOKEN"
curl "$BASE/entities/orders/items" -H "x-api-key: $ANYTHINK_API_KEY"
Authentication says who is calling. Permissions say what they may do. A user gets permissions from their role, and an API key carries the permissions picked when it was created. A caller without the permission for an endpoint gets 403 Forbidden. Never put an API key in a browser or mobile app. Apps should sign users in and call the API as them, as in Anythink SDK: sign-in and sessions.
The CLI and the local MCP server sign in as you, so they need no header. They use the project your active profile points at. See Install and use the Anythink CLI.
Your first requests
The five requests below cover most of what an app does. Each is shown with curl, with the CLI and as a prompt for an AI assistant over MCP. All three go to the same endpoints and return the same records.
Create a record
POST sends the field values as a JSON object.
The raw request:
curl -X POST "$BASE/entities/orders/items" \
-H "x-api-key: $ANYTHINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"reference": "A-1", "total": 10.5, "status": "paid", "placed_on": "2026-10-02", "paid": true}'
{
"locked": false,
"id": 1,
"created_at": "2026-10-05T09:12:41.813603Z",
"updated_at": "2026-10-05T09:12:41.813603Z",
"reference": "A-1",
"total": 10.5,
"status": "paid",
"placed_on": "2026-10-02T00:00:00",
"paid": true,
"__name": "A-1"
}
The status is 201 Created, and the Location header points at the new record. __name is the value of the entity's name field.
With the CLI
anythink data create orders --data '{"reference": "A-1", "total": 10.5, "status": "paid", "placed_on": "2026-10-02", "paid": true}'
Creating orders record...
✓ Record created in orders (id: 1).
The CLI then prints the new record as JSON.
With an AI assistant (MCP)
Create an order with reference A-1, a total of 10.50, status paid, placed on 2 October 2026 and paid.
The assistant uses the cli tool to run data create orders with those values and shows you the record it gets back.
To create several at once, send an array to /entities/orders/items/bulk. The response is the array of new records.
List records
GET on the collection returns a page of records, wrapped with paging details.
The raw request:
curl "$BASE/entities/orders/items" -H "x-api-key: $ANYTHINK_API_KEY"
{
"items": [
{ "locked": false, "id": 1, "reference": "A-1", "total": 10.5, "status": "paid", "paid": true, "__name": "A-1" },
{ "locked": false, "id": 2, "reference": "A-2", "total": 20, "status": "draft", "paid": false, "__name": "A-2" }
],
"page": 1,
"page_size": 25,
"total_items": 2,
"total_pages": 1,
"has_next_page": false,
"has_previous_page": false,
"retrieval_time": 5
}
The records above are shortened to the most useful fields. A real response includes every field you're allowed to read.
With the CLI
anythink data list orders --limit 10
The CLI prints a table. Add --json for the records as JSON.
With an AI assistant (MCP)
List my orders.
The assistant runs data list orders and summarises what comes back.
Get one record
Put the record's id on the end of the path. The response is the record on its own, without the paging wrapper. An unknown ID returns 404.
The raw request:
curl "$BASE/entities/orders/items/1" -H "x-api-key: $ANYTHINK_API_KEY"
With the CLI
anythink data get orders 1
With an AI assistant (MCP)
Show me order 1.
The assistant runs data get orders 1.
Update a record
PUT changes only the fields you send. Fields you leave out keep their values, so you don't need to send the whole record. There is no PATCH.
The raw request:
curl -X PUT "$BASE/entities/orders/items/1" \
-H "x-api-key: $ANYTHINK_API_KEY" \
-H "Content-Type: application/json" \
-d '{"status": "shipped"}'
The response is 204 No Content with no body. Read the record again if you need the new values.
With the CLI
anythink data update orders 1 --data '{"status": "shipped"}'
Updating orders/1...
✓ Record orders/1 updated.
With an AI assistant (MCP)
Mark order 1 as shipped.
The assistant runs data update orders 1 with the new status.
Delete a record
The raw request:
curl -X DELETE "$BASE/entities/orders/items/1" -H "x-api-key: $ANYTHINK_API_KEY"
The response is 204 No Content. Deleting is permanent.
With the CLI
anythink data delete orders 1 --yes
Deleting record...
✓ Record orders/1 deleted.
Without --yes the CLI asks you to confirm.
With an AI assistant (MCP)
Delete order 1.
The assistant runs data delete orders 1. Deleting is permanent, so check the order number in your prompt.
Page and sort results
The list endpoint takes these query parameters. The full table is in REST API reference.
| Parameter | Default | What it does |
|---|---|---|
page |
1 |
Page number, starting at 1. |
pageSize |
25 |
Records per page, from 1 to 1000. |
sortBy |
id |
The field to sort by. |
sortDirection |
asc |
asc or desc. |
fields |
all fields | Comma-separated fields to return. id, locked and __name always come back. |
pageSize, sortBy and sortDirection are camel case in the query string, even though the JSON is snake case.
curl "$BASE/entities/orders/items?sortBy=total&sortDirection=desc&pageSize=1&page=2&fields=reference,total" \
-H "x-api-key: $ANYTHINK_API_KEY"
{
"items": [
{ "locked": false, "id": 1, "reference": "A-1", "total": 10.5, "__name": "A-1" }
],
"page": 2,
"page_size": 1,
"total_items": 3,
"total_pages": 3,
"has_next_page": true,
"has_previous_page": true,
"retrieval_time": 5
}
Keep asking for the next page while has_next_page is true. In the CLI, anythink fetch calls any path with your project's base URL and signs the request for you, so the same query string works there:
anythink fetch "/entities/orders/items?sortBy=total&sortDirection=desc&pageSize=1&page=2"
Filter records
Add a field name and a value to the query string. A bare value means equals. Put an operator in front of the value for anything else. Filters on different fields combine with AND.
curl "$BASE/entities/orders/items?status=paid&total=GTE:10" -H "x-api-key: $ANYTHINK_API_KEY"
anythink data list orders --filter 'status=paid&total=GTE:10'
To put two conditions on one field, separate them with a comma. total=GTE:6,LT:30 means 6 or more and under 30. A filter on a field the entity doesn't have is ignored, so a misspelt name returns unfiltered records. If a total looks too high, check the field name first.
Filter operators reference lists every operator, how values are read for each field type and how to combine conditions.
Relationships in responses
When an entity has relationship fields, each record includes the related data under the field's name.
| Relationship | Shape under the field name |
|---|---|
| Many to one, one to one | The related record as an object, or null when it isn't set. |
| One to many, many to many | An object { "__count": n, "__data": [ … ] }. |
For an orders entity where each order has a customer:
{
"id": 3,
"reference": "A-3",
"customer": { "id": 1, "name": "Ada Lovelace", "__name": "Ada Lovelace" },
"__name": "A-3"
}
And the other way round, where each customer has many orders:
{
"id": 1,
"name": "Ada Lovelace",
"orders": {
"__count": 2,
"__data": [
{ "id": 3, "__name": "A-3" },
{ "id": 7, "__name": "A-7" }
]
}
}
By default a many-to-one or one-to-one field comes back expanded, while a one-to-many or many-to-many field comes back with each related record's id and __name. Use fields to ask for more or less, for example fields=reference,customer.name. The related records you get are filtered by the caller's permissions, so two callers can see different data for the same record. REST API reference covers expansion in full, and Connect entities with relationships shows how to model and write them.
Errors
Read the status code first. The body depends on where the error came from, so don't assume it's JSON.
| Status | Meaning | Example |
|---|---|---|
400 |
A bad parameter or body, a locked record, or a value the database refuses, such as a duplicate in a unique field. | 'pageSize' must be between 1 and 1000 |
401 |
No credentials, or credentials that aren't valid. | JSON with "title": "Unauthorized" and a traceId. |
403 |
The caller lacks the permission for this endpoint. | An empty body. |
404 |
The record doesn't exist, or the caller can't see it. | JSON with "title": "Not Found" and a traceId. |
405 |
The method isn't supported, such as PATCH. |
An empty body. |
429 |
Too many requests. Wait for retry_after seconds. |
JSON with retry_after. |
If a request that should work returns 403, check the caller's role or the API key's permissions before anything else. Log the traceId from error bodies with the request. It's what support needs to find a failure.
Know where your API is documented
The dashboard documents your own project's API from your current data model, so it includes the entities you created yesterday. See Your project's own API documentation for the interactive pages and the OpenAPI documents you can feed to a client generator.
Next steps
- REST API reference: every endpoint, parameter, limit and status code.
- Your project's own API documentation: browse and try your project's endpoints.
- Anythink SDK: sign-in and sessions: sign users in and call the API from a web app.
- Filter operators reference: every filter operator.
- API keys: create a key for a server or script.
- Roles and permissions: give your users access to the entities you create.