Candidates
GET /api/v1/candidates returns the candidates in your workspace a page at a time, and GET /api/v1/candidates/{id} returns one. Both need Read candidate data.
Overview
GET /api/v1/candidates lists the candidates in your workspace, and GET /api/v1/candidates/{id} returns one of them. Both need the Read candidate data permission, which Full access does not include. A key can hold it only while an admin has turned on Candidate data in the API in Settings → API. When an admin turns it off, every key loses it, and both operations answer 403 with the code insufficient-scope.
You get the candidates you can see in the app. A key limited to some teams gets fewer. It sees only candidates with an application on one of its teams, so it never sees a candidate in the talent pool or one you reach only through a privately shared application. Use a key with access to all your teams to read those. Authentication covers team access.
A candidate here carries the name, headline and location from the top of the candidate's page. The API leaves out the email address, the phone number, notes, files and everything else on the record.
Request
curl "https://attia.app/api/v1/candidates?limit=50" \ -H "Authorization: Bearer $ATTIA_API_KEY"All three query parameters are optional.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
limit | integer, 1 to 100 | 25 | How many candidates to return |
cursor | string | none | The next_cursor of the previous page |
updated_since | RFC 3339 timestamp | none | Only candidates updated at or after this time |
A parameter outside this table gets a 400 with the code invalid-request. So does a parameter sent twice.
Response
The answer holds the candidates in data, sorted by updated_at and then id, oldest change first. next_cursor is a string when more candidates exist and null on the last page.
{ "data": [ { "id": "0192f3b4-2c18-7a5e-9d07-6e1f4a8b2c93", "full_name": "Kari Eksempel", "headline": "Backend developer", "location": "Eksempelvik", "created_at": "2026-09-25T12:04:51.220417Z", "updated_at": "2026-10-01T09:42:10.118305Z" } ], "next_cursor": null}| Field | Type | Meaning |
|---|---|---|
id | UUID | The candidate's id in your workspace. It never changes |
full_name | string | The candidate's name |
headline | string or null | The line under the name, often the candidate's current role |
location | string or null | Where the candidate is, as the record states it |
created_at | timestamp | When the candidate was added to your workspace |
updated_at | timestamp | When the candidate last changed |
Version 1 can gain a field, so ignore one you do not know. Timestamps, nulls and enums describes each format.
Page through every candidate
Ask for a page, then send next_cursor back as cursor until it is null.
curl "https://attia.app/api/v1/candidates?limit=100&cursor=eyJ1IjoiMjAyNi0xMC0wMlQwODoxNTozMC40ODI5MTNaIiwiaSI6IjAxOTJmM2I0LTJjMTgtN2E1ZS05ZDA3LTZlMWY0YThiMmM5MyJ9" \ -H "Authorization: Bearer $ATTIA_API_KEY"The cursor stays stable while people edit candidates. A candidate edited during your read moves behind the cursor, and you may see it again on a later page. Keep the latest copy per id. Pagination explains the sort order.
Poll for changes
updated_since is inclusive. Send a time five minutes before the largest updated_at you have stored, and drop the repeats by id. Poll for changes explains why.
curl "https://attia.app/api/v1/candidates?updated_since=2026-10-01T09:37:10.118305Z" \ -H "Authorization: Bearer $ATTIA_API_KEY"A candidate who is deleted or erased stops appearing, and the list does not tell you. If you keep a copy, ask for each stored id now and then. A 404 means the candidate is gone for your key.
Get one candidate
GET /api/v1/candidates/{id} returns the candidate with that id. It takes no query parameters, so one gets a 400 with the code invalid-request.
curl "https://attia.app/api/v1/candidates/0192f3b4-2c18-7a5e-9d07-6e1f4a8b2c93" \ -H "Authorization: Bearer $ATTIA_API_KEY"The answer holds the candidate in data, with the same fields as a candidate in the list.
{ "data": { "id": "0192f3b4-2c18-7a5e-9d07-6e1f4a8b2c93", "full_name": "Kari Eksempel", "headline": "Backend developer", "location": "Eksempelvik", "created_at": "2026-09-25T12:04:51.220417Z", "updated_at": "2026-10-01T09:42:10.118305Z" }}A 404 with the code not-found means no candidate your key can see has that id. The candidate may not exist, may be deleted or erased, may belong to another workspace, or may sit outside your key's teams. Every one of these gets the same answer, so a 404 never tells you whether the candidate exists. An id that is not a UUID gets the same 404.