Search
The search endpoints run a keyword-and-location query against an Indeed country edition. POST /v1/search returns role summaries; POST /v1/search/with-details also fetches every result’s full role object server-side.
| Method | Path | Credits | Mode |
|---|---|---|---|
| POST | /v1/search | 1 per page | Sync at ≤3 pages, async above |
| POST | /v1/search/with-details | 1 per page + 1 per role | Always async |
curl -X POST "https://api.rolesapi.com/v1/search" \ -H "Authorization: Bearer rk_live_your_key" \ -H "Content-Type: application/json" \ -d '{"keyword": "backend engineer", "location": "Austin, TX", "max_pages": 2}'What are the body parameters?
Both endpoints take the same body:
| Parameter | Type | Required | Description |
|---|---|---|---|
keyword | string | Yes | Search terms, e.g. "backend engineer". |
location | string | Yes | City, state, or region, e.g. "Austin, TX". |
country | string | No | Indeed country edition code. Default us. |
sort | string | No | date or relevance. Default relevance. |
max_pages | integer | No | 1–20 result pages to fetch. Default 1. |
When does a search run sync vs. async?
POST /v1/search with max_pages of 3 or fewer responds synchronously with results in the body. Above 3 pages, it returns 202 with a job — the same async job flow as batch. POST /v1/search/with-details is always async, whatever the page count.
How are credits charged?
/v1/search: 1 credit per result page fetched./v1/search/with-details: 1 credit per page, plus 1 credit per role whose details are fetched.
Worked examples:
| Request | Result | Credits |
|---|---|---|
search, max_pages: 2 | 2 pages of summaries | 2 |
search, max_pages: 10 | 10 pages, async | 10 |
search/with-details, max_pages: 2, jobs return 30 roles | 2 pages + 30 details | 2 + 30 = 32 |
search/with-details, max_pages: 5, jobs return 70 roles | 5 pages + 70 details | 5 + 70 = 75 |
The role counts above are illustrative — how many roles a page holds depends on the query. Credits for details are charged per role actually processed, so short result sets cost less than the worst case.
What does a sync response look like?
{ "data": [ { "job_key": "a1b2c3d4e5f60718", "title": "Senior Backend Engineer", "company": { "name": "Acme Logistics", "rating": 4.1 }, "location": "Austin, TX", "remote": false, "posted_at": "2026-06-28", "url": "https://www.indeed.com/viewjob?jk=a1b2c3d4e5f60718", "country": "us" } ], "meta": { "total": 34, "count": 34, "limit": 34, "offset": 0, "has_more": false }, "request_id": "req_c31f8e02"}Summaries carry the fields visible on a results page. For salary, benefits, and the full description, fetch the role by job_key — see Roles — or use with-details.
What does an async response look like?
Both a deep search and every with-details call return HTTP 202:
{ "data": { "job_id": "5f0f6c2e-4b3a-4e9c-9c74-31d1f2a30c11", "status": "queued" }, "request_id": "req_77e0d1a9"}Poll GET /v1/jobs/{id} and page GET /v1/jobs/{id}/results. For with-details, results are full role objects — the Acme Logistics shape in Roles — and can be exported as CSV or NDJSON via output formats.
Errors: 400 invalid_request if keyword is missing, sort is not date/relevance, or max_pages is outside 1–20; 402 out_of_credits if your balance is zero.
Search or listings?
If your query is really “what was posted today / this week / remote,” the Listings presets encode those filters for you.