Documentation
Introduction
The Partner API gives approved organisations read access to open job listings, the companies behind them and aggregated market statistics. It is a JSON REST API over HTTPS.
Base URL: https://aresume.ai/api/v1. A machine-readable description is available at
https://aresume.ai/api/v1/openapi.json (OpenAPI 3.1), which you can import into Postman, Insomnia or a client generator.
Authentication
Create a key in the Partnership Portal and send it with every request:
Authorization: Bearer ares_live_...
The X-API-Key header is also accepted. Keys in the URL are not, because URLs end up in logs. Keep keys on your servers: the API does not send CORS headers, so browser code cannot call it directly.
Each key has scopes:
| Scope | Allows |
|---|---|
jobs:read | Search and read job listings |
companies:read | Search and read companies |
stats:read | Read market and salary statistics |
You can also restrict a key to specific IP addresses or ranges. Revoke a key at any time; it stops working immediately.
Requests and responses
- All endpoints use
GETand return JSON encoded in UTF-8. - Times are UTC in ISO 8601 (
2026-09-29T08:00:00Z); dates areYYYY-MM-DD. - Every response carries an
X-Request-Id. Include it when you contact us about a request. - Metered responses also carry
X-ARes-Units,X-ARes-CostandX-ARes-Balance. - The version is part of the path. Fields may be added to
v1without notice, so ignore fields you do not know; nothing is removed or renamed withinv1.
Example
curl "https://aresume.ai/api/v1/jobs?q=data+engineer&country=BE&limit=2" \ -H "Authorization: Bearer $ARES_API_KEY"
import os, requests
response = requests.get(
"https://aresume.ai/api/v1/jobs",
params={"q": "data engineer", "country": "BE", "limit": 2},
headers={"Authorization": f"Bearer {os.environ['ARES_API_KEY']}"},
timeout=30,
)
response.raise_for_status()
for job in response.json()["data"]:
print(job["title"], job["company"]["name"] if job["company"] else "")
// Node.js 18 or later, on your server
const response = await fetch("https://aresume.ai/api/v1/jobs?country=NL&limit=2", {
headers: { Authorization: `Bearer ${process.env.ARES_API_KEY}` },
});
if (!response.ok) throw new Error((await response.json()).error.message);
const { data, pagination } = await response.json();
Sample response
{
"data": [
{
"id": 201512,
"title": "Data Engineer",
"company": {"id": 88, "name": "Example Bank"},
"location": {"city": "Ghent", "region": "East Flanders", "country": "Belgium",
"country_code": "BE", "display": "Ghent, Belgium"},
"remote_type": "hybrid",
"employment_type": "full-time",
"experience_level": "mid",
"category": "Data Science",
"salary": null,
"skills": ["python", "sql", "airflow"],
"summary": "Build and run the data platform behind our retail products...",
"posted_date": "2026-09-28",
"updated_at": "2026-09-29T06:12:44Z",
"apply_url": "https://careers.example.com/jobs/1234",
"url": "https://aresume.ai/jobs/201512",
"source": "company_careers",
"urgently_hiring": false
}
],
"pagination": {"limit": 2, "has_more": true, "next_cursor": "eyJpIjoy..."}
}
Pagination and sync
List endpoints return up to limit results (default 20, maximum set by your plan). When pagination.has_more is true, pass pagination.next_cursor as cursor with the same filters to get the next page. Cursors are signed and only valid for the sort order that produced them.
To keep a local copy current, request sort=updated_asc with updated_after set to the time of your last sync, and page until has_more is false. updated_at only moves when a field you receive changes or a closed job reopens; routine re-checks of the source do not move it, so each sync only returns (and charges for) jobs that actually changed. A listing that stops appearing in GET /jobs has closed.
Rate limits
Limits apply per organisation, across all your keys:
| Plan | Per minute | Per day | Page size |
|---|---|---|---|
| Sandbox | 10 | 100 | 20 |
| Standard | 60 | 20,000 | 50 |
| Scale | 300 | 200,000 | 100 |
Responses include X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix time) for the current minute, plus X-RateLimit-Daily-Limit and X-RateLimit-Daily-Remaining. The daily count resets at 00:00 UTC. Over a limit, the API answers 429 with a Retry-After header in seconds; wait that long before retrying.
Pricing and billing
Each endpoint costs a number of units (listed with each endpoint below). Lookups and statistics cost a fixed number of units per call. Searches cost their units once per started block of 10 results returned, so a page of 1 to 10 results costs 1 unit and a page of 50 costs 5; an empty page costs 1. Your plan sets the price per unit and how many units are free each calendar month (UTC). Only successful responses (2xx) are charged; errors and throttled calls are free. Every response reports its cost in the X-ARes-Units and X-ARes-Cost headers.
| Plan | Price per unit (USD) | Free units per month | Availability |
|---|---|---|---|
| Sandbox | Free | 250 | Open |
| Standard | $0.018 | 0 | Coming soon |
| Scale | $0.012 | 0 | Coming soon |
On a free plan the free units are also the monthly limit; when they run out the API answers 402 with free_quota_exhausted until the 1st of the next month (UTC).
Paid plans open later; they will draw from a prepaid balance that you top up by card in the portal.
GET /account/usage returns your balance and usage and is free.
Errors
Errors return a JSON body:
{"error": {"code": "invalid_parameter", "message": "limit must be between 1 and 50 on your plan.",
"param": "limit", "request_id": "9f1c..."}}
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_parameter | A parameter is missing or invalid; see param. |
| 401 | missing_api_key, invalid_api_key | No key was sent, or the key is unknown. |
| 402 | insufficient_balance, free_quota_exhausted | Add credit, or move to a paid plan. |
| 403 | key_revoked, scope_missing, ip_not_allowed, terms_not_accepted, account_suspended, no_plan | The key or account is not allowed to make this call. |
| 404 | not_found | The resource or endpoint does not exist. |
| 429 | rate_limited, daily_quota_exceeded | Wait for Retry-After seconds. |
| 500 | internal_error | Our fault. Retry with backoff and quote the request ID if it persists. |
Endpoints
/api/v1/jobs
1 unit per 10 results · jobs:read
Search open jobs
Open listings, newest first by default. Use sort=updated_asc with updated_after to keep a local copy in sync.
| Parameter | Type | Description |
|---|---|---|
q |
string | Words that must all appear in the job title. Example: data engineer |
country |
string | Country name or ISO 3166-1 alpha-2 code. Example: BE |
city |
string | City name. Example: Ghent |
remote |
string | Work type. One of: remote, hybrid, on-site. |
employment_type |
string | Contract type. One of: full-time, part-time, contract, temporary, internship. |
experience_level |
string | Seniority. One of: entry, mid, senior, executive. |
category |
string | Category name, for example "Data Science". |
company_id |
integer | Only jobs from this company. |
posted_after |
string | Posted on or after this date (YYYY-MM-DD). Example: 2026-09-01 |
updated_after |
string | Changed at or after this UTC time (ISO 8601). Example: 2026-09-29T00:00:00Z |
has_salary |
boolean | true = only jobs that state a salary. |
sort |
string | Order of results. Default posted_desc. One of: posted_desc, updated_asc. |
include |
string | Set to "description" to include the full plain-text description. One of: description. |
limit |
integer | Results per page (1 to your plan maximum). Default 20. Example: 20 |
cursor |
string | Opaque cursor from the previous page (pagination.next_cursor). |
/api/v1/jobs/{job_id}
1 unit · jobs:read
Get one job
A single open job with its full plain-text description.
| Parameter | Type | Description |
|---|---|---|
job_id required |
integer (path) | Job ID. |
/api/v1/companies
1 unit per 10 results · companies:read
Search companies
Companies with at least one open job you can access, with their open-job count.
| Parameter | Type | Description |
|---|---|---|
q |
string | Part of the company name. Example: bank |
country |
string | Only count jobs in this country (name or ISO code). |
limit |
integer | Results per page (1 to your plan maximum). Default 20. Example: 20 |
cursor |
string | Opaque cursor from the previous page (pagination.next_cursor). |
/api/v1/companies/{company_id}
1 unit · companies:read
Get one company
A company with its open-job count and the countries and categories it hires in.
| Parameter | Type | Description |
|---|---|---|
company_id required |
integer (path) | Company ID. |
/api/v1/stats/markets
3 units · stats:read
Open jobs by market
Counts of open jobs grouped by country, category, work type, contract type or source, with the number posted in the last 7 days.
| Parameter | Type | Description |
|---|---|---|
group_by |
string | Grouping. Default country. One of: country, category, remote, employment_type, source. |
country |
string | Limit to one country (name or ISO code). |
q |
string | Words that must all appear in the job title. |
/api/v1/stats/salaries
3 units · stats:read
Salary distribution
Yearly salary percentiles for open jobs that state a salary, per currency. Monthly, weekly, daily and hourly figures are converted to yearly.
| Parameter | Type | Description |
|---|---|---|
q |
string | Words that must all appear in the job title. Example: product manager |
country |
string | Country name or ISO code. Example: NL |
category |
string | Category name. |
/api/v1/account/usage
Free
Your usage and balance
Your plan, balance, remaining free units and the last 30 days of usage. Free of charge.
Data sources and attribution
The API currently includes listings from: Posted on ARes, Company careers feeds. Each listing's source field tells you where it came from.
When you show a listing, name ARes as the source and link to its url (the listing on ARes) or its apply_url. See the API terms for how long you may keep data.
Salary statistics treat salaries without a stated period as yearly, unless the amount is clearly hourly (under 300) or monthly (under 15,000) in USD, EUR, GBP, CHF, CAD, AUD, NZD or SGD.
Changelog
- 2026-09-30:
v1released.