Developers

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.

ParameterTypeDefaultMeaning
limitinteger, 1 to 10025How many candidates to return
cursorstringnoneThe next_cursor of the previous page
updated_sinceRFC 3339 timestampnoneOnly 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}
FieldTypeMeaning
idUUIDThe candidate's id in your workspace. It never changes
full_namestringThe candidate's name
headlinestring or nullThe line under the name, often the candidate's current role
locationstring or nullWhere the candidate is, as the record states it
created_attimestampWhen the candidate was added to your workspace
updated_attimestampWhen 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.