Start in one minute
Search works without a key or sign-up. Copy and run it in your terminal:
curl "https://www.joonow.com/api/v1/jobs?q=analista%20de%20dados&l=Campinas&work=Remote&limit=5"
The response has the jobs, the total, the next page cursor and how to credit Joonow:
{
"data": [
{
"id": "k3j9x2m4pq",
"title": "Analista de dados",
"url": "https://www.joonow.com/pt-br/vaga/analista-de-dados-k3j9x2m4pq",
"applyUrl": "https://www.joonow.com/go/k3j9x2m4pq",
"company": { "id": "0f5rb3kg49", "name": "Acme", "url": "https://www.joonow.com/pt-br/empresas/acme-0f5rb3kg49", "logo": null },
"location": { "city": "Campinas", "region": "SP", "country": "BR" },
"workModel": "Remote",
"employmentType": "FullTime",
"seniority": "Professional",
"salary": { "min": 6000, "max": 8000, "currency": "BRL", "period": "Month" },
"language": "pt",
"postedAt": "2026-10-28T12:00:00Z",
"native": false
}
],
"total": 214,
"totalIsLowerBound": false,
"nextCursor": "djEuMi4xYzJm...",
"attribution": { "text": "Vagas via Joonow", "url": "https://www.joonow.com/pt-br/vagas?q=analista%20de%20dados" }
}
Endpoints
All of them are GET and return JSON. Links in the responses are absolute and canonical: use them as they come.
| Endpoint | What it returns |
|---|---|
GET / | Search of open jobs, with filters and cursor pagination. |
GET / | One job, with the description as text and as clean HTML. |
GET / | A company with open jobs: totals, cities, job titles, published salaries and the first page of jobs. |
GET / | A company's open jobs, with cursor pagination. |
GET / | The latest edition of the Joonow Job Market Index (or the month's preliminary numbers). |
GET / | One monthly edition of the Index. |
GET / | The list of published editions. |
GET / | latest Joonow Trends pieces (data stories, daily brief, Candidate Voice Survey) |
GET / | what changed for a role and a city |
GET / | The API description in OpenAPI 3.1, to generate clients and test. |
Search filters
The same as the website search. Filters with several values accept the key repeated (work=Remote&work=Hybrid) or comma-separated values; any of them matches.
| Parameter | Values |
|---|---|
q | Keywords or a short phrase: job title, skill or company (up to 200 characters). |
l | City or Brazilian state (UF), for example Campinas or SP. Up to 12. |
work | Remote, Hybrid, OnSite or Anywhere (remote jobs open to anyone; with country, also remote jobs from abroad open to that country). |
type | FullTime, PartTime, Contractor, Temporary, Intern, Volunteer, PerDiem or Other. |
level | Apprentice, Intern, Entry, Operational, Professional, Management or Executive. |
days | Posted in the last N days, from 1 to 365. |
salary | Minimum monthly salary in the search currency (currency); only jobs with a published salary in that currency match, no exchange rates. |
sort | relevance (default), date (newest) or salary (highest salary). |
lang | Language of the jobs: pt, en or es. Without it, all. |
limit | Jobs per page, from 1 to 50 (default 20). |
cursor | The nextCursor value from the previous response. |
country | Country of the jobs (ISO 3166-1 alpha-2, Americas only: BR, US, MX, CA, AR...), like the country's section on the website. Without it, all of the Americas. |
currency | Currency of salary and of sort=salary (ISO 4217: BRL, USD, MXN, CAD...). Without it, the country's currency; without country, BRL. |
Pagination
Every response has nextCursor. For the next page, repeat the same search with cursor=value. A cursor only works for the same search and the same limit, and null means the last page. As on the website, search goes up to 50 pages: to go further, narrow the filters.
Errors
Errors use the problem+json format (RFC 9457), always with a stable code field and, for a parameter, the param field.
| code | Status | When it happens |
|---|---|---|
invalid_ | 400 | A parameter has an invalid value (the param field says which). |
invalid_ | 400 | The cursor is malformed or belongs to another search. |
invalid_ | 401 | The key sent does not exist, was revoked or is malformed. |
job_ | 404 | The job does not exist or is already closed. |
company_ | 404 | The company does not exist or has no open jobs. |
edition_ | 404 | That edition of the Index is not published yet. |
rate_ | 429 | Over the per-minute limit. Wait for the Retry-After time. |
daily_ | 429 | The key is over its daily quota, which resets at midnight UTC. |
search_ | 503 | Search is down for a moment. Try again after Retry-After. |
Keys and limits
Without a key, the API and the MCP server already work, with limits per IP. With a free key, the limits go up and you can follow the usage in your account.
| Access | Per minute | Per day |
|---|---|---|
| No key, API | 10 per IP | no daily quota |
| No key, MCP | 30 per IP | no daily quota |
| Free key | 60 | 10,000 |
Create the key in your account and send it in the X-Api-Key header (or Authorization: Bearer). It is shown only once; keep it somewhere safe and never publish it. Create my key
curl -H "X-Api-Key: jnk_..." "https://www.joonow.com/api/v1/jobs/k3j9x2m4pq"
Every response has RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; with a key, also X-Daily-Limit and X-Daily-Remaining. Over the limit, the response is 429 with Retry-After.
MCP server for AI assistants
The Model Context Protocol (MCP) lets AI assistants look up Joonow in the middle of a conversation: "find remote data analyst jobs" becomes a real search, with each job's link. The server is remote, read-only and needs no sign-in.
Address (streamable HTTP):
https://www.joonow.com/mcp
Tools
search_jobs: searches jobs by title, country, place, work model, contract, seniority, date and salary.get_job: returns one full job by id or link.get_company: returns a company that is hiring, with numbers and its first jobs.market_index: returns the month's Joonow Job Market Index numbers.market_insights: what changed for a role or a city: the role x-ray, who is hiring and the Joonow Trends stories.latest_stories: the latest Joonow Trends pieces, each with three quotable sentences and the link.
How to connect
Claude Code
claude mcp add --transport http joonow https://www.joonow.com/mcp
claude mcp add --transport http joonow https://www.joonow.com/mcp --header "X-Api-Key: jnk_..."
VS Code (.vscode/mcp.json)
{
"servers": {
"joonow": { "type": "http", "url": "https://www.joonow.com/mcp" }
}
}
Cursor (~/.cursor/mcp.json)
{
"mcpServers": {
"joonow": { "url": "https://www.joonow.com/mcp" }
}
}
Claude: in Settings › Connectors, add a custom connector with the address above.
ChatGPT: in Settings › Apps & Connectors, turn on developer mode and create a connector with the address above.
With a key, the MCP limits are your key's: send the X-Api-Key header, as in the Claude Code example.
How to credit Joonow
When you show the data, show "Jobs via Joonow" with a link next to the jobs and use each job's link (url) and apply link (applyUrl) as the API returns them. Refresh stored jobs within 24 hours. Reselling the data, in whole or in part, is not allowed.