For information about how REST API works, see rest main page.
Every endpoint uses the same status codes, the same error body format and the same query limits. This page describes them once.
Status codes
| Status | Meaning | What to do |
|---|---|---|
200 OK |
The request succeeded. The body is JSON. | — |
400 Bad Request |
Your request was rejected — an unknown column, a malformed where, a missing mandatory field, a query the object doesn't support, or a rate limit. The body explains which. |
Fix the request. Retrying the same request will fail again. |
401 Unauthorized |
The API key is wrong or revoked, or the request wasn't sent over HTTPS. | Check the key, and check you are using https://. |
500 Server Error |
Something failed on our side. The body contains a description. | Retry once; if it persists, send us the response body. |
503 Service Unavailable |
A temporary database condition. | Wait a few seconds and retry. Safe to retry automatically with backoff. |
Error bodies are plain text, not JSON — only successful responses are JSON. So when a client fails to parse a response as JSON, the status code is the thing to look at.
Common error messages
These come back with 400:
Unknown column: {name}/No such column in table: {name}/Requested column doesn't exist: {name}— the column you asked for incolumns,whereor the request body isn't exposed by that object. Each object's page lists its columns.Column "{name}" is mandatory— a required field was missing from a POST body.Column {name} is read-only— that field can be read but not written.Couldn't interpret "where"/Error in "where"/Error in "where": columns count doesn't match values count— the path parameter syntax is malformed.Couldn't interpret "limit"—limitwas not a number, or notoffset,limit.Only can select up to 1000 rows— see Query limits below.Cannot do this operation on whole table…/Queries to this object must use indices…/Such query to this object is not supported— see Query limits below.Row not found— aPUTorDELETEaddressed a record id that doesn't exist.Couldn't parse input application/json or application/x-www-form-urlencoded— the request body wasn't valid JSON, and theContent-Typewasn't form-encoded either.Too many simultaneous requests— writes to users are serialized per account and arrived too fast. Retry with backoff.
Query limits
At most 1000 rows per request. /limit/1000 is the maximum. Use /limit/{OFFSET},{LIMIT} to page through more.
Large tables must be queried by an index. Objects backed by big tables (users, sessions, interactions) won't run a query that would scan the whole table. In practice:
- Selecting by an indexed column (for users:
user_id,internal_id,email,fb_id,data_last_modified) is fast and unrestricted. - Selecting by a non-indexed column (for example a user's first name) is a full scan. Personyze will either refuse the query, or serve only the first 50 rows of it.
- When a query is refused, the error message names the columns that do have indices — use one of them in
whereororder_by.
Small catalog-style objects (user_lists, placeholders, conditions, users_columns) have no such restriction — you can read them whole.
Writing safely
POSTto users merges into an existing profile when an identifier matches, so re-sending the same record is safe and won't create duplicates.PUTandDELETEapply to every row matched by the path parameters. ADELETEwith nowhereis not silently interpreted as "nothing" — always send an explicit condition.PUTandDELETEreturn the number of affected rows, so0means your condition matched nothing.
Retrying
503, and 500 on a write, are the only statuses worth retrying automatically. Use exponential backoff starting around one second. Because writes to users merge rather than duplicate, a retry after an ambiguous failure is safe.