API
API keys, abilities, rate limits, pagination and error shapes.
Authentication
Every request carries an API key as a bearer token. Keys are created in the account area under Settings → API keys, with the New key button. The key is shown once, on creation, and only a hash is stored, so copy it there and then.
A key looks like 12|kX9fQ2...: a number, a pipe, then a long random string.
Send all of it, number and pipe included, after Bearer.
curl https://ydomain.com/api/v1/domains \
-H "Authorization: Bearer 12|kX9fQ2..." \
-H "Accept: application/json"
A missing or unknown key gives 401:
{
"message": "Unauthenticated."
}
Abilities
A key holds an explicit list of abilities. Every endpoint declares the one it needs, so a read-only key can never write, whatever it is pointed at.
| Ability | Grants |
|---|---|
domains:read |
Read listings |
domains:write |
Create, update and delete listings |
leads:read |
Read leads and their message threads |
leads:write |
Change lead status and reply to a buyer |
webhooks:manage |
List, create and delete webhook endpoints |
GET /me works with any valid key and reports the abilities the current key
holds. Using an endpoint outside them gives 403:
{
"message": "Invalid ability provided."
}
Give a key only what it needs. A script that imports listings does not need
leads:read, and a reporting job needs nothing beyond the two read abilities.
Expiry and revoking
A key can be given an expiry when it is created; after that moment it is refused like an unknown key. Keys can be revoked one by one under Settings → API keys, or all at once under Settings → Security. Changing your password revokes every key as well.
Rate limits
- 120 requests per minute per key.
- Unauthenticated requests are limited to 20 per minute per IP address.
Every response carries the usual headers:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
Over the limit you get 429 with a Retry-After header in seconds:
{
"message": "Too Many Attempts."
}
Back off for that long rather than retrying immediately; retries inside the window count against the limit too.
Pagination
List endpoints are paginated. per_page defaults to 25 and is capped at 100.
curl "https://ydomain.com/api/v1/domains?per_page=2&page=2" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json"
{
"data": [
{ "domain": "example.net" },
{ "domain": "example.org" }
],
"links": {
"first": "https://ydomain.com/api/v1/domains?page=1",
"last": "https://ydomain.com/api/v1/domains?page=74",
"prev": "https://ydomain.com/api/v1/domains?page=1",
"next": "https://ydomain.com/api/v1/domains?page=3"
},
"meta": {
"current_page": 2,
"from": 3,
"last_page": 74,
"per_page": 2,
"to": 4,
"total": 148
}
}
Walk a list by following links.next until it is null, rather than counting
pages yourself: a listing added while you are paging shifts the offsets.
Errors
| Status | Means |
|---|---|
401 |
No key, an unknown key, or an expired one |
403 |
The key lacks the ability, or the record belongs to someone else |
404 |
No such record on your account |
422 |
The body failed validation |
429 |
Rate limited; wait for Retry-After |
5xx |
Our side. Retry with backoff; a POST is not automatically idempotent |
A 422 names every field that failed:
{
"message": "The extension field is required.",
"errors": {
"extension": ["The extension field is required."],
"price": ["The price field must be a number."]
}
}
Note that a record belonging to another account gives 404, not 403: the API
does not confirm that an id exists elsewhere.