API reference
The Reach API gives you read-only REST access to your account's data over JSON, authenticated with a bearer token. Every endpoint is a GET; the deals endpoint is reserved and returns a 501 until a later release.
The Reach API is read-only. Every route under /api/v1 accepts only GET requests, returns JSON, and requires a bearer token. Create a token first (see API tokens), then pass it on every request to the base URL https://reach.mercleo.com/api/v1.
Authenticating requests
Send the token as a bearer credential in the Authorization header on every request. Tokens carry the prefix mrl_.
Authorization: Bearer mrl_<your-token>Tokens are account-scoped and grant only the scopes you select when you create them. Each endpoint requires a specific scope, named in the reference below. If the header is missing or malformed, the request returns 401 missing_token. A revoked, expired, or unknown token returns 401 invalid_token, and a call that requires a scope the token does not carry returns 403 insufficient_scope.
Rate limits
The API allows 100 requests per minute per account. Every successful response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers so you can track your remaining budget.
Exceeding the limit returns 429 rate_limited with a Retry-After header giving the backoff in seconds. Wait that long before retrying.
Endpoints
Every endpoint is a GET. Each one names the scope the token must carry.
Contacts
GET /api/v1/contacts: a paginated list of contacts. Scopecontacts:read. Query params:limit(1–500, default 50),cursor(opaque),lifecycle_stage(matches the lifecycle stage label),tag(tag UUID), andq(substring match across name and email).GET /api/v1/contacts/[id]: a single contact by id. Scopecontacts:read. A contact that does not belong to your account returns404 not_found.
curl -H 'Authorization: Bearer mrl_…' \
'https://reach.mercleo.com/api/v1/contacts?limit=50'Companies
GET /api/v1/companies: a paginated list of companies (organizations). Scopecompanies:read. Query params:limit(1–500, default 50),cursor(opaque), andq(substring match on name and domain).GET /api/v1/companies/[id]: a single company by id. Scopecompanies:read. A company that does not belong to your account returns404 not_found.
Lists
GET /api/v1/lists: every contact list owned by your account. Scopelists:read. Returns each list's id, name, description, and type. There is no single-list GET; only the collection and its resolved contacts below.GET /api/v1/lists/[id]/contacts: the resolved member set for one list. Scopelists:read. Returns members for static lists and evaluated matches for dynamic lists. Query params:limit(1–1000, default 100) andcursor(opaque). A list id that does not belong to your account returns404 not_found.
Objects
These two endpoints expose the schema of your account's custom object types: their fields, and the stage sets and stages defined on them.
GET /api/v1/objects: every custom object type installed on the account. Scopeobjects:read. No query params. Returns each type's key, singular and plural display names, icon, colour, default view, primary relation, and whether it's a system template.GET /api/v1/objects/[key]: one object type by its key (for examplegiftorsales_deal), including its full field list and its stage sets with their stages. Scopeobjects:read. An unknown key returns404 object_type_not_found.
Records
GET /api/v1/records: a paginated list of records for one object type. Scoperecords:read. Requires?type=<object-type-key>; omitting it returns400 type_required, and a type key not installed on your account returns404 object_type_not_found. Other query params:limit(1–500, default 50) andcursor(opaque). Each record includes its field values, its stage (if it belongs to a stage set), and its links to a contact, organization, or household.GET /api/v1/records/[id]: a single record by id, including its relations to other records. Scoperecords:read. An unknown id returns404 record_not_found.
Deals
GET /api/v1/deals and GET /api/v1/deals/[id] are reserved and not yet implemented. Both routes exist and the deals:read scope is grantable today, but every call currently returns 501 not_implemented. Do not build against it yet.
Paginating with cursors
Paginated responses carry a pageInfo object that includes a nextCursor value. To fetch the next page, pass that value back as the cursor query param on your next request. When nextCursor is null, you have reached the end of the result set.
Error shape
Errors return a consistent JSON envelope with a machine-readable code and a human-readable message.
{
"error": {
"code": "invalid_token",
"message": "Token is invalid, revoked, or expired"
}
}| Status | Code | Meaning |
|---|---|---|
| 400 | type_required | Records collection called without ?type= |
| 400 | invalid_cursor | Cursor is malformed |
| 401 | missing_token | Authorization header missing or malformed |
| 401 | invalid_token | Token is invalid, revoked, or expired |
| 403 | insufficient_scope | Token lacks the scope the endpoint requires |
| 404 | not_found | Contact, company, or list is missing or not in your account |
| 404 | object_type_not_found | No custom object type with that key on this account |
| 404 | record_not_found | No record with that id on this account |
| 429 | rate_limited | Over 100 req/min; see Retry-After header |
| 501 | not_implemented | Deals endpoint; reserved for a later release |
Updated