API DOCUMENTATION / API REFERENCE
OpenAPI reference
Every route, parameter, request body, response and schema in the live OpenAPI specification.
Endpoints
Every route in the live OpenAPI specification (16 operations), with its parameters, request body and responses. Base URL: https://api.headcountlabs.com. The machine-readable source is https://api.headcountlabs.com/openapi (OpenAPI 3.1.0).
| Method and path | Summary |
|---|---|
GET /v1/companies | Browse companies |
POST /v1/companies/search | Search companies |
GET /v1/companies/{companyId} | Get a company by UUID |
GET /v1/companies/{companyId}/jobs | List a company's jobs |
GET /v1/jobs | List public jobs |
POST /v1/jobs/search/es_dsl | Search public jobs with Elasticsearch-style clauses |
POST /v1/jobs/exports | Create a bulk jobs export |
GET /v1/jobs/exports | List recent jobs exports |
GET /v1/jobs/exports/{exportId} | Get a jobs export |
GET /v1/jobs/exports/{exportId}/files/{n} | Download a gzip JSON Lines export file |
GET /v1/jobs/changes | List public job changes |
GET /v1/jobs/count | Count public jobs |
GET /v1/jobs/{jobId} | Get a job by ID |
PUT /v1/account/overage | Turn overage billing on or off and set its monthly limit |
GET /v1/usage/requests | List customer request events |
GET /v1/usage/analytics | Read customer daily usage |
Overview
Search and sync job postings collected from employers' own job sources, with company records.
Authentication: send your API key as a bearer token, Authorization: Bearer jc_live_<id>.<secret> (keys issued before October 2026 start with ar_live_ and still work). Create keys in your account. A missing or wrong key returns 401 invalid_api_key.
Paging: list endpoints return next_cursor; pass it back as cursor for the next page. Every page of one listing reads the same catalog snapshot (snapshot_watermark), so results don't shift under you. A cursor from an older snapshot or with different filters returns 400; start again from the first page.
Keeping in sync: pull GET /v1/jobs/changes with since for the first call, then keep passing resume_cursor as after to receive created, updated and expired jobs in order.
Limits: your plan sets a request rate and a monthly allowance of returned records. Only successful responses count, and each job or company returned counts as one record; counts and usage reports are free. Going over returns 429 (rate_limited or data_limit_exceeded) with a Retry-After header in seconds. Paid plans can turn on overage (PUT /v1/account/overage) to go past the monthly job allowance, optionally with a monthly overage limit in dollars. Overage never adds API requests: the monthly request allowance still applies.
Errors always look like {"error": {"code", "message", "request_id"}}. Quote request_id when you contact support. 503 catalog_unavailable means the jobs catalog is being published; retry shortly. 504 query_timeout means a search ran longer than 10 seconds and was stopped; it uses no records. Narrow the filters and try again.
GET /v1/companies
Browse companies
Lists companies from one immutable registry release.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
name | query | string; min length 1, max length 200 | Case-insensitive canonical-name substring. |
limit | query | integer; min 1, max 100, default 50 | Page size, 1-100. Defaults to 50. |
cursor | query | string; min length 1, max length 2048 | The next_cursor from the previous page. It pins the company release it started on. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A page of public company records. | CompanyPage |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
POST /v1/companies/search
Search companies
Combines verified name, country, and status filters with AND. Search uses the same UUID company records and release-pinned cursor as company browse.
Request body (JSON, required)
Schema: CompanySearchRequest.
| Field | Type | Required | Description |
|---|---|---|---|
name | string; min length 1, max length 200 | no | Case-insensitive canonical-name substring after trimming. |
country | string | no | ISO 3166-1 alpha-2 code, case-insensitive. |
status | string: "active", "retired", "merged" | no | |
limit | integer; min 1, max 100, default 50 | no | |
cursor | string; min length 1, max length 2048 | no |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A page of matching public company records. | CompanyPage |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
GET /v1/companies/{companyId}
Get a company by UUID
Parameters
| Name | In | Type | Description |
|---|---|---|---|
companyId (required) | path | CompanyId | Atlas company ID (lowercase UUID). A merged company's old ID redirects (308) to its current ID. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A public company record. | object |
308 | A merged company ID redirects to its current UUID. | |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
404 | The company or customer-owned API key was not found. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
GET /v1/companies/{companyId}/jobs
List a company's jobs
Lists the company's public jobs from the latest published jobs catalog, with the same filters, order and cursor rules as GET /v1/jobs. Resolves the company UUID and redirects merged IDs. Requires an active company registry release and a published jobs catalog, and responds 503 while either is missing.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
companyId (required) | path | CompanyId | Atlas company ID (lowercase UUID). A merged company's old ID redirects (308) to its current ID. |
fields | query | string | Comma list of the job properties to return; repeat the parameter or separate names with commas. Allowed names are job_id, currentness, stale, company_id, employer_name, title, description, work_arrangement, locations, job_first_seen_at, curated_at, job_updated_at, postings and company. job_id is always returned. Without fields the whole job is returned. An unknown name returns 400. It changes only the shape of each job, never which jobs match. |
Responses
| Status | Meaning | Body |
|---|---|---|
308 | A merged company ID redirects to its current UUID. | |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
404 | The company or customer-owned API key was not found. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
504 | The query did not finish within 10 seconds (code query_timeout). No records are charged. Narrow the filters, for example add a state, title, or employer, and try again. | ErrorEnvelope |
GET /v1/jobs
List public jobs
Returns included public jobs from the latest published jobs catalog. A search with q, title or description is ranked by relevance, with title matches weighted ten times description matches; any other list, or sort=updated, is newest job_updated_at first. Ties break by job_id, descending. Without a currentness or active filter only current jobs are returned. Every page of one listing reads the same catalog snapshot: next_cursor is bound to that snapshot, the filters and the limit, and a cursor from an older snapshot or a different query returns 400. Responds 503 catalog_unavailable while no jobs catalog has been published.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
company | query | string; min length 1, max length 200 | Employer name contains this text. Case-insensitive. 1-200 characters. A term of 1-2 characters matches the whole employer name or its first word instead (`hp` finds "HP Inc." but not "Shop HP Repairs"). |
company_id | query | CompanyId | Only jobs of this company, by its Atlas company ID (lowercase UUID). |
title | query | string; min length 1, max length 200 | Job title contains these words as one phrase, in this order (full-text match). "senior software engineer" does not match "Software Engineer, Senior" or "Senior Staff Software Engineer"; use title_advanced, such as senior AND "software engineer", to match the words in any order. 1-200 characters. |
title_advanced | query | string; min length 1, max length 200 | Boolean title search: the words may appear in any order. Supports AND, OR and NOT keywords, &, | and !, parentheses, prefix terms such as engineer:*, and quoted phrases such as "product manager". Prefer it over title when job titles vary in word order, for example senior AND ("software engineer" OR "software developer"). A positive search term is required. |
description_advanced | query | string; min length 1, max length 200 | Boolean description search. Supports AND, OR and NOT keywords, &, | and !, parentheses, prefix terms such as engineer:*, and quoted phrases such as "product manager". A positive search term is required. |
q | query | string; min length 1, max length 200 | Keyword search: the phrase appears in the title or description. Results are ranked by relevance unless sort=updated. 1-200 characters. |
location | query | string; min length 1, max length 200 | Any job location contains this text (city, region or country as written). Case-insensitive. 1-200 characters. |
state | query | string; min length 1, max length 200 | Two-letter subdivision code. On its own it means a U.S. state (state=CA is California only); combine with country for another country's subdivision (country=CA&state=BC is British Columbia). |
country | query | string | Two-letter ISO country code, for example US or CA. Case-insensitive. Send up to 10 codes separated by commas to match jobs in any of them. |
msa | query | string | 5-digit CBSA code of a metropolitan or micropolitan statistical area, for example 12420 for Austin-Round Rock-San Marcos, TX. Matches any job location whose msa.cbsa_code equals it. |
work_arrangement | query | string: "remote", "hybrid", "in_person" | One of remote, hybrid or in_person, as the source states it. Jobs whose source doesn't say are not matched. |
remote_type | query | string: "remote", "hybrid", "in_person" | Alias for work_arrangement; do not send both. |
employment_type | query | string; min length 1, max length 200 | Employment type as published, for example full_time or part_time. Exact match, case-insensitive. Send up to 10 values separated by commas to match jobs with any of them. |
source | query | string; min length 1, max length 200 | The source vendor, such as greenhouse or smartrecruiters. Board IDs are not exposed. |
currentness | query | string; default "current" | Comma list of current, uncertain and not_current. |
active | query | boolean | true maps to currentness=current and false to currentness=not_current; do not send with currentness. |
posted_since | query | string (date) or string (date-time) | Posted on or after this time, by the date the source published. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). A plain date means the start of that day in UTC. |
posted_until | query | string (date) or string (date-time) | Posted on or before this time. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). A plain date means the end of that day in UTC. |
published_since | query | string (date) or string (date-time) | Compatibility alias for posted_since; do not send both. |
first_seen_since | query | string (date) or string (date-time) | First collected by Atlas on or after this time. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). |
updated_since | query | string (date) or string (date-time) | Job record last changed on or after this time; use it for incremental pulls. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). |
curated_since | query | string (date) or string (date-time) | Entered the final dataset on or after this time. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). |
job_id | query | string (uuid) | One job by its job ID (a lowercase UUID). |
description | query | string; min length 1, max length 200 | Words that must appear together in the job description. Use description_advanced for a Boolean expression. |
city | query | string; min length 1, max length 200 | City name. Matches any job location whose city contains this text, case-insensitive. Combine with state and country to narrow it. |
exclude_source | query | string; max length 200 | Comma list of up to 10 source vendors or source types to leave out. A job still matches when another of its postings comes from a source not on the list. |
source_type | query | string; min length 1, max length 200 | The kind of job source, as in postings[].source_data.source_type. Exact match, case-insensitive. |
source_class | query | string: "ats", "employer-site", "government-exchange", "aggregator", "staffing-network", "dataset", "purchased", "third-party-api", "unknown" | The class of job source a posting came from, for example ats for an applicant tracking system board or employer-site for a company's own careers page. |
posting_id | query | string; min length 1, max length 200 | The job that holds this posting, by Atlas's posting ID. |
source_posting_id | query | string; min length 1, max length 200 | Jobs with a posting that has this source posting ID (the job source's own ID for the listing). |
posting_status | query | string: "listed", "no_longer_listed" | Jobs with a posting in this state. listed means the source still shows it; no_longer_listed means a completed read no longer found it. |
stale | query | boolean | true for jobs first listed more than 30 days ago (the earlier of the date Atlas first saw the job and the source's posted date), updated daily; false for the rest. Stale jobs stay in results; pass stale=false to leave them out. |
company_linked | query | boolean | true for jobs linked to a company record (company_id is set); false for jobs with no linked company. |
has_description | query | boolean | true for jobs with a known description; false for jobs without one. |
has_location | query | boolean | true for jobs with at least one known location; false for jobs with none. |
has_pay | query | boolean | true for jobs with a posting that publishes pay; false for jobs with a posting that does not. |
has_posting_url | query | boolean | true for jobs with a posting that has a known listing URL. |
has_application_url | query | boolean | true for jobs with a posting that has a known application URL. |
pay_currency | query | string | ISO 4217 currency code. Jobs with a posting whose published pay is in this currency. |
pay_min | query | number; min 0, max 1000000000 | Lowest yearly pay. Matches postings whose pay floor is at least this amount. Pay is compared per year (hourly x 2,080, monthly x 12); pay with an unknown interval never matches. Amounts are not converted between currencies, so pair it with pay_currency. Up to two decimal places. |
pay_max | query | number; min 0, max 1000000000 | Highest yearly pay. Matches postings whose pay ceiling is at most this amount, compared per year the same way as pay_min. Must not be below pay_min. |
first_seen_until | query | string (date) or string (date-time) | First collected by Atlas on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
updated_until | query | string (date) or string (date-time) | Last changed on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
curated_until | query | string (date) or string (date-time) | Entered the final dataset on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
last_fetch_since | query | string (date) or string (date-time) | Jobs with a posting whose job source was last read completely on or after this time. Accepts an RFC 3339 timestamp or a plain date. |
last_fetch_until | query | string (date) or string (date-time) | Jobs with a posting whose job source was last read completely on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
application_check | query | string: "available", "closed", "broken", "inconclusive" | Jobs with a posting whose latest application check had this result. available means an application could still be started. |
application_check_since | query | string (date) or string (date-time) | Jobs with a posting whose latest application check ran on or after this time. Accepts an RFC 3339 timestamp or a plain date. |
include | query | string | Comma list containing company and/or source_data. company adds the linked public company record; source_data adds each posting's source data. |
fields | query | string | Comma list of the job properties to return; repeat the parameter or separate names with commas. Allowed names are job_id, currentness, stale, company_id, employer_name, title, description, work_arrangement, locations, job_first_seen_at, curated_at, job_updated_at, postings and company. job_id is always returned. Without fields the whole job is returned. An unknown name returns 400. It changes only the shape of each job, never which jobs match. |
description_format | query | string: "text", "html"; default "text" | text returns descriptions as plain text. html returns 503 because source HTML descriptions are not published yet. |
limit | query | integer; min 1, max 1000, default 50 | Page size, 1-250, or 1-1,000 when fields is exactly job_id. Defaults to 50. |
cursor | query | string; min length 1, max length 2048 | The next_cursor from the previous page. It is tied to the same filters, page size, sort and catalog snapshot; start again from the first page if you get a 400 for a stale cursor. |
sort | query | string: "relevance", "updated" | relevance ranks by how well q, title and description match, and is the default when one of them is set; it returns 400 without one. updated lists the most recently updated jobs first, and is the default otherwise. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A page of public job records. | JobListResponse |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
504 | The query did not finish within 10 seconds (code query_timeout). No records are charged. Narrow the filters, for example add a state, title, or employer, and try again. | ErrorEnvelope |
POST /v1/jobs/search/es_dsl
Search public jobs with Elasticsearch-style clauses
Translates the supported Elasticsearch-style query subset to the same filters as POST /v1/jobs/search. All must and filter clauses are AND-ed. should is supported only for term or match clauses on the same country or employment_type field, including when must/filter is also present. Range gt is treated as inclusive gte, and lt as inclusive lte. The response, cursor, request charge, limits, and errors match POST /v1/jobs/search. Page size is 1-250, or 1-1000 when fields is exactly job_id.
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
query | object | yes | One supported clause: match_all, bool, term, terms, match, match_phrase, range, exists, query_string, or simple_query_string. Unsupported clauses and fields return 400 invalid_request. See the operation description and JobCensus API documentation for the accepted field list. |
limit | integer; min 1, max 1000, default 50 | no | Page size 1-250. Values 251-1000 are accepted only when fields is exactly job_id. |
cursor | string; min length 1, max length 2048 | no | |
sort | string: "relevance", "updated" | no | |
fields | string or array of string | no | |
include | array of string: "company", "source_data" | no | |
description_format | string: "text", "html"; default "text" | no |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A page of public job records. | JobListResponse |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
504 | The query did not finish within 10 seconds (code query_timeout). No records are charged. Narrow the filters, for example add a state, title, or employer, and try again. | ErrorEnvelope |
POST /v1/jobs/exports
Create a bulk jobs export
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
q | string | no | |
fields | array of string | no | |
include | array of string: "company", "source_data" | no | |
description_format | string: "text", "html"; default "text" | no | |
sort | string: "relevance", "updated" | no | |
max_records | integer; min 1, max 100000, default 100000 | no |
Responses
| Status | Meaning | Body |
|---|---|---|
202 | Export accepted | JobsExport |
400 | Invalid export request | |
401 | Invalid API key | |
409 | The account already has a queued or running export | |
429 | Monthly request allowance is insufficient | |
503 | Export service is unavailable |
GET /v1/jobs/exports
List recent jobs exports
Responses
| Status | Meaning | Body |
|---|---|---|
200 | Exports for the authenticated account, newest first, limited to 20 | object |
401 | Invalid API key | |
503 | Export service is unavailable |
GET /v1/jobs/exports/{exportId}
Get a jobs export
Parameters
| Name | In | Type | Description |
|---|---|---|---|
exportId (required) | path | string (uuid) |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | Export status and file metadata | JobsExport |
401 | Invalid API key | |
404 | Export does not exist or belongs to another account | |
503 | Export service is unavailable |
GET /v1/jobs/exports/{exportId}/files/{n}
Download a gzip JSON Lines export file
Parameters
| Name | In | Type | Description |
|---|---|---|---|
exportId (required) | path | string (uuid) | |
n (required) | path | integer; min 1 |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | Gzip-compressed JSON Lines file | string (binary) (application/gzip) |
401 | Invalid API key | |
404 | File is unknown, not ready, expired, or belongs to another account | |
503 | Export service is unavailable |
GET /v1/jobs/changes
List public job changes
Lists created, updated and expired job events from the latest published jobs catalog, in event order. Send exactly one of since or after: start with since, then pass resume_cursor as after. A cursor from an older catalog snapshot returns 400. Responds 503 catalog_unavailable while no jobs catalog has been published, and 503 service_unavailable when the catalog changes during the request (retry).
Parameters
| Name | In | Type | Description |
|---|---|---|---|
after | query | string; min length 1, max length 2048 | The resume_cursor from your previous changes call. Send exactly one of after or since. |
since | query | string (date-time) | Start of the change window: changes strictly after this RFC 3339 timestamp. Use it for the first call, then continue with after. |
types | query | string; default "created,updated,expired" | Comma list of created, updated and expired. Defaults to all three. |
limit | query | integer; min 1, max 1000, default 100 | Events per page, 1-1,000. Defaults to 100. |
company_id | query | CompanyId | Only changes for this company's jobs (lowercase UUID). |
include | query | string: "source_data" | source_data adds allowlisted source metadata to each job in the events. The source payload and internal extraction details are not published. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A page of job changes. | JobsChangesResponse |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
504 | The query did not finish within 10 seconds (code query_timeout). No records are charged. Narrow the filters, for example add a state, title, or employer, and try again. | ErrorEnvelope |
GET /v1/jobs/count
Count public jobs
Counts jobs matching the same filters as GET /v1/jobs in the latest published jobs catalog, with that catalog's snapshot watermark. Responds 503 catalog_unavailable while no jobs catalog has been published.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
company | query | string; min length 1, max length 200 | Employer name contains this text. Case-insensitive. 1-200 characters. A term of 1-2 characters matches the whole employer name or its first word instead (`hp` finds "HP Inc." but not "Shop HP Repairs"). |
company_id | query | CompanyId | Only jobs of this company, by its Atlas company ID (lowercase UUID). |
title | query | string; min length 1, max length 200 | Job title contains these words as one phrase, in this order (full-text match). "senior software engineer" does not match "Software Engineer, Senior" or "Senior Staff Software Engineer"; use title_advanced, such as senior AND "software engineer", to match the words in any order. 1-200 characters. |
title_advanced | query | string; min length 1, max length 200 | Boolean title search: the words may appear in any order. Supports AND, OR and NOT keywords, &, | and !, parentheses, prefix terms such as engineer:*, and quoted phrases such as "product manager". Prefer it over title when job titles vary in word order, for example senior AND ("software engineer" OR "software developer"). A positive search term is required. |
description_advanced | query | string; min length 1, max length 200 | Boolean description search. Supports AND, OR and NOT keywords, &, | and !, parentheses, prefix terms such as engineer:*, and quoted phrases such as "product manager". A positive search term is required. |
q | query | string; min length 1, max length 200 | Keyword search: the phrase appears in the title or description. 1-200 characters. |
location | query | string; min length 1, max length 200 | Any job location contains this text (city, region or country as written). Case-insensitive. 1-200 characters. |
state | query | string; min length 1, max length 200 | Two-letter subdivision code. On its own it means a U.S. state (state=CA is California only); combine with country for another country's subdivision (country=CA&state=BC is British Columbia). |
country | query | string | Two-letter ISO country code, for example US or CA. Case-insensitive. Send up to 10 codes separated by commas to match jobs in any of them. |
msa | query | string | 5-digit CBSA code of a metropolitan or micropolitan statistical area, for example 12420 for Austin-Round Rock-San Marcos, TX. Matches any job location whose msa.cbsa_code equals it. |
work_arrangement | query | string: "remote", "hybrid", "in_person" | One of remote, hybrid or in_person, as the source states it. Jobs whose source doesn't say are not matched. |
remote_type | query | string: "remote", "hybrid", "in_person" | Alias for work_arrangement; do not send both. |
employment_type | query | string; min length 1, max length 200 | Employment type as published, for example full_time or part_time. Exact match, case-insensitive. Send up to 10 values separated by commas to match jobs with any of them. |
source | query | string; min length 1, max length 200 | The source vendor, such as greenhouse or smartrecruiters. Board IDs are not exposed. |
currentness | query | string; default "current" | Comma list of current, uncertain and not_current (combined with OR). Defaults to current. A job is current when a completed read of its job source listed it within the last 31 days and no check has found it closed. |
active | query | boolean | true maps to currentness=current; do not send with currentness. |
posted_since | query | string (date) or string (date-time) | Posted on or after this time, by the date the source published. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). A plain date means the start of that day in UTC. |
posted_until | query | string (date) or string (date-time) | Posted on or before this time. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). A plain date means the end of that day in UTC. |
published_since | query | string (date) or string (date-time) | Compatibility alias for posted_since; do not send both. |
first_seen_since | query | string (date) or string (date-time) | First collected by Atlas on or after this time. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). |
updated_since | query | string (date) or string (date-time) | Job record last changed on or after this time; use it for incremental pulls. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). |
curated_since | query | string (date) or string (date-time) | Entered the final dataset on or after this time. Accepts an RFC 3339 timestamp with a time zone (2026-09-21T00:00:00Z) or a plain date (2026-09-21). |
job_id | query | string (uuid) | One job by its job ID (a lowercase UUID). |
description | query | string; min length 1, max length 200 | Words that must appear together in the job description. Use description_advanced for a Boolean expression. |
city | query | string; min length 1, max length 200 | City name. Matches any job location whose city contains this text, case-insensitive. Combine with state and country to narrow it. |
exclude_source | query | string; max length 200 | Comma list of up to 10 source vendors or source types to leave out. A job still matches when another of its postings comes from a source not on the list. |
source_type | query | string; min length 1, max length 200 | The kind of job source, as in postings[].source_data.source_type. Exact match, case-insensitive. |
source_class | query | string: "ats", "employer-site", "government-exchange", "aggregator", "staffing-network", "dataset", "purchased", "third-party-api", "unknown" | The class of job source a posting came from, for example ats for an applicant tracking system board or employer-site for a company's own careers page. |
posting_id | query | string; min length 1, max length 200 | The job that holds this posting, by Atlas's posting ID. |
source_posting_id | query | string; min length 1, max length 200 | Jobs with a posting that has this source posting ID (the job source's own ID for the listing). |
posting_status | query | string: "listed", "no_longer_listed" | Jobs with a posting in this state. listed means the source still shows it; no_longer_listed means a completed read no longer found it. |
stale | query | boolean | true for jobs first listed more than 30 days ago (the earlier of the date Atlas first saw the job and the source's posted date), updated daily; false for the rest. Stale jobs stay in results; pass stale=false to leave them out. |
company_linked | query | boolean | true for jobs linked to a company record (company_id is set); false for jobs with no linked company. |
has_description | query | boolean | true for jobs with a known description; false for jobs without one. |
has_location | query | boolean | true for jobs with at least one known location; false for jobs with none. |
has_pay | query | boolean | true for jobs with a posting that publishes pay; false for jobs with a posting that does not. |
has_posting_url | query | boolean | true for jobs with a posting that has a known listing URL. |
has_application_url | query | boolean | true for jobs with a posting that has a known application URL. |
pay_currency | query | string | ISO 4217 currency code. Jobs with a posting whose published pay is in this currency. |
pay_min | query | number; min 0, max 1000000000 | Lowest yearly pay. Matches postings whose pay floor is at least this amount. Pay is compared per year (hourly x 2,080, monthly x 12); pay with an unknown interval never matches. Amounts are not converted between currencies, so pair it with pay_currency. Up to two decimal places. |
pay_max | query | number; min 0, max 1000000000 | Highest yearly pay. Matches postings whose pay ceiling is at most this amount, compared per year the same way as pay_min. Must not be below pay_min. |
first_seen_until | query | string (date) or string (date-time) | First collected by Atlas on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
updated_until | query | string (date) or string (date-time) | Last changed on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
curated_until | query | string (date) or string (date-time) | Entered the final dataset on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
last_fetch_since | query | string (date) or string (date-time) | Jobs with a posting whose job source was last read completely on or after this time. Accepts an RFC 3339 timestamp or a plain date. |
last_fetch_until | query | string (date) or string (date-time) | Jobs with a posting whose job source was last read completely on or before this time. Accepts an RFC 3339 timestamp or a plain date. |
application_check | query | string: "available", "closed", "broken", "inconclusive" | Jobs with a posting whose latest application check had this result. available means an application could still be started. |
application_check_since | query | string (date) or string (date-time) | Jobs with a posting whose latest application check ran on or after this time. Accepts an RFC 3339 timestamp or a plain date. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | Matching job count and snapshot watermark. | JobCountResponse |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
504 | The query did not finish within 10 seconds (code query_timeout). No records are charged. Narrow the filters, for example add a state, title, or employer, and try again. | ErrorEnvelope |
GET /v1/jobs/{jobId}
Get a job by ID
Returns one job with all its linked postings from the latest published jobs catalog. Responds 503 catalog_unavailable while no jobs catalog has been published.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
jobId (required) | path | JobId | Atlas job ID (lowercase UUID). A merged job redirects (308) to the job it merged into; a withdrawn job returns 410. |
fields | query | string | Comma list of the job properties to return; repeat the parameter or separate names with commas. Allowed names are job_id, currentness, stale, company_id, employer_name, title, description, work_arrangement, locations, job_first_seen_at, curated_at, job_updated_at, postings and company. job_id is always returned. Without fields the whole job is returned. An unknown name returns 400. It changes only the shape of each job, never which jobs match. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A public job record with all linked postings. | JobDetailResponse |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
504 | The query did not finish within 10 seconds (code query_timeout). No records are charged. Narrow the filters, for example add a state, title, or employer, and try again. | ErrorEnvelope |
PUT /v1/account/overage
Turn overage billing on or off and set its monthly limit
Overage is off by default: at the monthly job limit (plus any unused jobs carried over) requests stop with 429 data_limit_exceeded. Turn it on to keep going past the plan, billed at month end at the plan's price per 1,000 jobs (5,000 plan $3.00; 10,000 $2.70; 20,000 $2.40; 50,000 $2.10; 100,000 $1.88; 250,000 $1.65; 500,000 $1.50), rounded up to the cent. monthlyLimitUsd is an optional monthly overage limit in US dollars (a positive number with at most two decimals, up to 100000). With no limit, which is the default, overage has no ceiling. With a limit, once the month's overage dollars reach it requests stop with 429 data_limit_exceeded, nothing is billed above it, and a page that would cross it is refused whole. A limit of N dollars allows floor(N x 100 x 1000 / the plan's cents per 1,000 jobs) overage jobs: $1.00 on the 5,000 plan allows 333. null clears the limit and leaving the field out keeps the stored one. Overage does not add API requests: the monthly request allowance still applies with overage on. Needs a paid plan with an active subscription: the free plan answers 403 upgrade_required with an upgrade_url, and a plan without an active paid subscription answers 403 no_active_subscription. The same change is available to agents as the set_overage MCP tool. This request is not counted against the request windows or the monthly allowance.
Request body (JSON, required)
| Field | Type | Required | Description |
|---|---|---|---|
enabled | boolean | no | |
monthlyLimitUsd | number or null; above 0, max 100000 | no | Monthly overage limit in US dollars; null clears it (no limit). Omit to keep the stored value. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | The overage setting and this month's overage use. | OverageStatus |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
403 | upgrade_required (free plan; the error carries upgrade_url) or no_active_subscription. | ErrorEnvelope |
409 | overage_item_conflict. The subscription already carries another plan's overage price; contact support. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
GET /v1/usage/requests
List customer request events
Lists the authenticated customer's admitted requests. The cursor freezes admission membership for one hour; settlement status stays current.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
from (required) | query | string (date-time) | Inclusive UTC RFC 3339 timestamp. |
to (required) | query | string (date-time) | Exclusive UTC RFC 3339 timestamp; at most 31 days after from. |
route_class | query | string: "jobs.list", "jobs.detail", "jobs.changes", "jobs.count", "companies.jobs", "companies.list", "companies.detail", "companies.search", "usage.requests", "usage.analytics" | Only requests of this kind, for example jobs.list, jobs.count or companies.detail. |
api_key_id | query | string; min length 1, max length 200 | Only requests made with this key (one of your own keys). |
limit | query | integer; min 1, max 100, default 50 | Rows per page, 1-100. Defaults to 50. |
cursor | query | string; min length 1, max length 8192 | The next_cursor from the previous page. Usage cursors expire after one hour. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | A page of request events. | UsageRequestsPage |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
404 | The company or customer-owned API key was not found. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
GET /v1/usage/analytics
Read customer daily usage
Reports up to 100,000 unique events and 1,000 groups in a period of at most 31 days. Request metrics use admission day; record metrics use finalization day.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
from (required) | query | string (date-time) | Inclusive UTC RFC 3339 timestamp. |
to (required) | query | string (date-time) | Exclusive UTC RFC 3339 timestamp; at most 31 days after from. |
route_class | query | string: "jobs.list", "jobs.detail", "jobs.changes", "jobs.count", "companies.jobs", "companies.list", "companies.detail", "companies.search", "usage.requests", "usage.analytics" | Only requests of this kind, for example jobs.list, jobs.count or companies.detail. |
api_key_id | query | string; min length 1, max length 200 | Only requests made with this key (one of your own keys). |
group_by | query | string: "route_class", "status_class", "api_key_id" | Group the totals by route_class, status_class or api_key_id. Omit it for one overall total. |
Responses
| Status | Meaning | Body |
|---|---|---|
200 | Daily usage groups. | UsageAnalytics |
400 | The request parameters or body are invalid. | ErrorEnvelope |
401 | A valid bearer API key is required. | ErrorEnvelope |
404 | The company or customer-owned API key was not found. | ErrorEnvelope |
422 | The report exceeded a complete-result limit. | ErrorEnvelope |
429 | A request window or monthly returned-record allowance was reached. | ErrorEnvelope |
503 | The required service or publication is unavailable. | ErrorEnvelope |
Schemas
Every object returned or accepted by the routes above, with each field and its type.
JobsExport
| Field | Type | Required | Description |
|---|---|---|---|
export_id | string (uuid) | yes | |
status | string: "queued", "running", "completed", "failed", "expired" | yes | |
created_at | string (date-time) | yes | |
completed_at | string or null (date-time) | yes | |
expires_at | string or null (date-time) | yes | |
record_count | integer; min 0 | yes | |
snapshot_watermark | string or null | yes | |
request | object | yes | |
files | array of object | yes | |
error | string | no |
CompanyId
Type: string (uuid)
JobId
Type: string (uuid)
FieldStateSourceOmitted
The source did not publish this field.
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes |
FieldStateSourceUnmapped
The source published a value outside this field's vocabulary. The value stays null and evidence keeps the source's own words.
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes | |
value | null | yes | |
evidence | array of string | yes |
FieldStateExtractionFailed
The source published this field but Atlas could not read it.
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes |
FieldStateKnownString
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes | |
value | string | yes |
FieldStateDerivedString
A value computed from source data by a fixed rule, never by a model. Evidence names the rule's input.
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes | |
value | string | yes | |
evidence | array of string | yes |
FieldStateString
Type: FieldStateKnownString or FieldStateSourceOmitted or FieldStateSourceUnmapped or FieldStateExtractionFailed or FieldStateDerivedString
FieldStateKnownWorkArrangement
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes | |
value | string: "remote", "hybrid", "in_person" | yes |
FieldStateDerivedWorkArrangement
A value computed from source data by a fixed rule, never by a model. Evidence names the rule's input.
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes | |
value | string: "remote", "hybrid", "in_person" | yes | |
evidence | array of string | yes |
FieldStateWorkArrangement
Type: FieldStateKnownWorkArrangement or FieldStateSourceOmitted or FieldStateSourceUnmapped or FieldStateExtractionFailed or FieldStateDerivedWorkArrangement
FieldStateKnownObject
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes | |
value | object | yes |
FieldStateDerivedObject
A value computed from source data by a fixed rule, never by a model. Evidence names the rule's input.
| Field | Type | Required | Description |
|---|---|---|---|
state | any | yes | |
value | object | yes | |
evidence | array of string | yes |
FieldStateObject
Type: FieldStateKnownObject or FieldStateSourceOmitted or FieldStateSourceUnmapped or FieldStateExtractionFailed or FieldStateDerivedObject
JobPosting
| Field | Type | Required | Description |
|---|---|---|---|
posting_id | string | yes | Atlas's ID for this posting (one public listing of the job on one job source). |
source | string | yes | The job source vendor, such as greenhouse or smartrecruiters. |
source_posting_id | string | yes | The job source's own ID for this posting. |
posting_status | string: "listed", "no_longer_listed" | yes | listed while the source still shows the posting; no_longer_listed once a completed fetch no longer finds it. |
posting_url | FieldStateString | yes | The posting's public page on the job source. |
application_url | FieldStateString | yes | Where to apply for this posting, when the source gives an application link. |
location | FieldStateObject | yes | The posting's location as the source published it (city, region, country). When the posting itself states a US postal code that is real and lies in the stated state, the object also has zip, the 5-digit ZIP (ZIP+4 is cut to 5). zip is absent for a city-only location, a street address without a ZIP, a remote posting and any non-US location; it is never derived from a city or a point. |
pay | FieldStateObject | yes | Pay as the source published it (minimum, maximum, currency and period), not converted. |
source_posted_at | string or null (date-time) | yes | The date the job source says the posting was published. |
first_seen_at | string (date-time) | yes | When Atlas first collected this posting. |
last_completed_fetch_at | string or null (date-time) | yes | When Atlas last read the posting's job source completely. |
latest_application_check | object or null | yes | Atlas's latest check of whether an application can still be started (available, closed, broken or inconclusive) and when it ran. |
attribution | object | yes | What the job source's rights record says about crediting it. null means the rights record does not say. When required is true, show text and link with the job. |
source_data | object | no | Allowlisted source metadata, present only when requested. The source payload and internal extraction details are not published. |
Job
| Field | Type | Required | Description |
|---|---|---|---|
job_id | JobId | yes | The combined JobCensus job ID. One job links every posting of the same opening. |
merged_into_job_id | JobId | no | Present on merged-job redirects. |
currentness | string: "current", "uncertain", "not_current" | yes | current while a linked posting is listed and accepting applications; uncertain when Atlas cannot confirm that; not_current when every linked posting is no longer listed. |
stale | boolean | yes | true when the job was first listed more than 30 days ago: the earlier of job_first_seen_at and the source's posted date is over 30 days old. Updated daily. A label only; the job stays listed. |
company_id | CompanyId or null | yes | The Atlas company record. Null when the job's employer is not yet linked to a company record; employer_name then carries the name the source published. |
employer_name | FieldStateString | yes | The employer name. |
title | FieldStateString | yes | The job title. |
description | FieldStateString | yes | The job description as plain text. |
work_arrangement | FieldStateWorkArrangement | yes | remote, hybrid or in_person, as the source states it. |
locations | array of FieldStateObject | yes | Every location of the job, combined from its linked postings. A known location value holds the address fields (street, city, region, postal_code, country); zip, the 5-digit US ZIP the posting states for that place when it states one that is real and in the stated state (the key is absent otherwise); and, when Atlas could place the location, county_fips (5-digit US county FIPS code, a string such as 48453) and msa (cbsa_code, cbsa_title and type, metro or micro, of the metropolitan or micropolitan statistical area; null when the location is in no statistical area). |
job_first_seen_at | string (date-time) | yes | When Atlas first collected any posting of this job. |
curated_at | string (date-time) | yes | When the job first entered the final dataset. |
job_updated_at | string (date-time) | yes | When the job record last changed; sort=updated orders by this. |
postings | array of JobPosting | yes |
JobDetailResponse
| Field | Type | Required | Description |
|---|---|---|---|
data | Job | yes |
JobListResponse
| Field | Type | Required | Description |
|---|---|---|---|
data | array of Job | yes | |
next_cursor | string or null | yes | |
snapshot_watermark | string (date-time) | yes |
JobChange
JobsChangesResponse
| Field | Type | Required | Description |
|---|---|---|---|
data | array of JobChange | yes | |
resume_cursor | string | yes | |
has_more | boolean | yes | |
snapshot_watermark | string (date-time) | yes |
JobCountResponse
| Field | Type | Required | Description |
|---|---|---|---|
count | integer; min 0 | yes | |
snapshot_watermark | string (date-time) | yes |
Company
| Field | Type | Required | Description |
|---|---|---|---|
company_id | CompanyId | yes | |
name | string | yes | |
country | string or null | yes | ISO 3166-1 alpha-2 code, or null when the registry country is unknown. |
status | string: "active", "retired", "merged" | yes | |
region | string or null | yes | State or province code from the same government address as country, or null when unknown. |
address_source | string or null: "sec", "dol_form5500", null | yes | Which government address gave country and region: sec (SEC business address) or dol_form5500 (Form 5500 plan sponsor address). Null when no government address is known. |
identifiers | array of object | yes | Public registry identifiers, each from a US-government public source. Empty when none is known. |
industries | array of object | yes | Industry codes, each from a US-government public source. Empty when none is known. |
CompanyPage
| Field | Type | Required | Description |
|---|---|---|---|
data | array of Company | yes | |
next_cursor | string or null | yes |
CompanySearchRequest
| Field | Type | Required | Description |
|---|---|---|---|
name | string; min length 1, max length 200 | no | Case-insensitive canonical-name substring after trimming. |
country | string | no | ISO 3166-1 alpha-2 code, case-insensitive. |
status | string: "active", "retired", "merged" | no | |
limit | integer; min 1, max 100, default 50 | no | |
cursor | string; min length 1, max length 2048 | no |
ErrorEnvelope
| Field | Type | Required | Description |
|---|---|---|---|
error | object | yes |
UsageRequestRow
| Field | Type | Required | Description |
|---|---|---|---|
request_id | string | yes | |
api_key_id | string | yes | |
route_class | string | yes | |
admitted_at | string (date-time) | yes | |
finalized_at | string or null | yes | |
state | string | yes | |
http_status | integer or null | yes | |
request_charged | boolean | yes | |
records_returned | integer; min 0 | yes | |
policy_id | string or null | yes | |
policy_version | integer or null | yes | |
settlement_policy_id | string or null | yes | |
settlement_policy_version | integer or null | yes |
OverageStatus
| Field | Type | Required | Description |
|---|---|---|---|
overage_enabled | boolean | yes | |
overage_monthly_limit_usd | number or null | yes | The monthly overage limit in US dollars; null when the customer set none. |
overage_limit_remaining_usd | number or null | yes | Dollars of the limit not yet used this month; null when there is no limit. |
overage_jobs_used | integer | yes | Jobs used this UTC month past the plan limit and the carried-over jobs. |
overage_usd | number | yes | This month's overage so far at the plan's rate, rounded up to the cent. |
UsageRequestsPage
| Field | Type | Required | Description |
|---|---|---|---|
data | array of UsageRequestRow | yes | |
next_cursor | string or null | yes | |
admissions_captured_at | string (date-time) | yes |
UsageDailyGroup
| Field | Type | Required | Description |
|---|---|---|---|
day | string (date) | yes | |
group | string | yes | |
request_events | integer | yes | |
charged_requests | integer | yes | |
pending_requests | integer | yes | |
rate_limited_requests | integer | yes | |
usage_limited_requests | integer | yes | |
http_status_2xx | integer | yes | |
http_status_3xx | integer | yes | |
http_status_4xx | integer | yes | |
http_status_5xx | integer | yes | |
error_rate | number or null; min 0, max 1 | yes | Share of admitted requests in this day and group that returned a 4xx or 5xx response, rounded to four decimal places; null when no requests were admitted. |
returned_records | integer | yes | |
job_records_returned | integer | yes | |
company_records_returned | integer | yes |
UsageAnalytics
| Field | Type | Required | Description |
|---|---|---|---|
query_timestamp | string (date-time) | yes | |
time_basis | object | yes | |
group_by | string or null: "route_class", "status_class", "api_key_id", null | yes | |
data | array of UsageDailyGroup | yes |
Continue in the reference
Check related request behavior before you build.
View route status and methods Discuss API requirements