API reference
Jobs
Search active job postings with a Query Spec tree and read the postings you select. Openings are a demand signal: who is hiring, for what, where, and at what pay.
- Routes
- 2
- Search
- /v1/jobs/query
- Search returns
- encrypted IDs
- Detail batch
- up to 100 IDs
- POST
/v1/jobs/queryQuery Spec search - POST
/entity/v1/jobs/detail-by-idFetch job records
Search, then fetch
Search never returns postings. It returns encrypted string job IDs, and you pass up to 100 of them to the detail route to read the records. There is no one-shot endpoint, so plan for two calls.
# 1. Search. Returns encrypted IDs, never postings.
curl -X POST "https://mira-api.metix.ai/v1/jobs/query" \
-H "Authorization: Bearer $METIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"where": {"all": [
{"field": "title", "match": "machine learning platform"},
{"field": "country_iso_2", "eq": "US"},
{"field": "posted", "gte": "now-30d"},
{"any": [
{"field": "seniority", "eq": "Mid-Senior level"},
{"field": "seniority", "eq": "Director"}
]}
]},
"size": 25
}'
# 2. Read the postings, up to 100 IDs per call.
curl -X POST "https://mira-api.metix.ai/entity/v1/jobs/detail-by-id" \
-H "Authorization: Bearer $METIX_KEY" \
-H "Content-Type: application/json" \
-d '{
"job_ids": ["Jb71xKcAoP", "Jb0mQ4ZtRe"],
"_source": ["id", "title", "company_name", "location", "seniority", "posted"]
}'Two calls, always
When to use it
- Measure hiring demand by role, function, seniority, company, or location.
- Find who is hiring for a capability, then pivot to the companies and people behind it.
- Filter by salary band or posting date to separate live demand from stale listings.
Request parameters and limits
- Parameter
- where
- Type
- object
- Notes
- Query Spec tree. Required by /v1/jobs/query
- Parameter
- size
- Type
- integer (1-10000)
- Notes
- Job IDs in one page. Omitting it returns 100
- Parameter
- after
- Type
- string
- Notes
- Cursor. Pass the next value from the previous response
- Parameter
- job_ids
- Type
- string[] (1-100)
- Notes
- IDs from a search, sent to the detail route
- Parameter
- _source / source
- Type
- array, object, or boolean
- Notes
- Optional field selection on detail
- One operator per leaf. A salary band is an all of two leaves, not gte and lte in one node.
- A field name that is not in this dataset's list is refused, so a typo is visible rather than returning a record without it. The three datasets do not share one vocabulary.
- Search returns at most 10,000 IDs per page and 100 when size is omitted; detail accepts at most 100 IDs per request.
- Jobs declare no scope nodes. One document is one posting, so there is nothing to group.
- The response carries IDs and a cursor, not a match count. Page until next is absent if you need the size of a set.
- The index holds daily active postings. A posting closed since your search can come back in not_found.
Credit cost
Queryable fields
These are the only names a leaf may put in field. The grammar around them, including composition, one operator per leaf, and relative dates, is on the Query Spec page. What a response may contain is a separate, wider list: a field can be returnable without being queryable.
titledescriptioncompany_nameWhat the posting is and who posted it.
titledescriptioncompany_nameOperators: match, eq, in, exists
{
"where": {
"all": [
{
"field": "title",
"match": "backend engineer"
},
{
"field": "application_active",
"eq": true
}
]
}
}seniorityemployment_typefunctionsindustriesHow the posting is classified.
seniorityemployment_typefunctionsindustriesOperators: match, eq, in, exists
{
"where": {
"all": [
{
"field": "seniority",
"eq": "Mid-Senior level"
}
]
}
}citystatecountrylocationregionsWhere the job is. country takes a name; country_iso_2 takes the code.
citystatecountrylocationregionsOperators: match, eq, in, exists
{
"where": {
"all": [
{
"field": "country",
"match": "China"
},
{
"field": "city",
"match": "Shanghai"
},
{
"field": "regions",
"match": "Asia"
}
]
}
}country_iso_2application_activeISO 3166-1 alpha-2 code such as US. application_active is true on every posting in the index today, so it narrows nothing.
country_iso_2application_activeOperators: eq, in, exists
{
"where": {
"all": [
{
"field": "application_active",
"eq": true
}
]
}
}salary_minsalary_maxAdvertised pay. exists separates postings that state a salary from those that do not.
salary_min, salary_max read on a record; filtering on them is being improved.
salary_minsalary_maxOperators: gte, lte, exists
Ask for salary_min and salary_max in _source and they come back on the record as normal. Using them as a filter answers with an error for now, rather than a set that looks right and is not, while we make the comparison reliable. Narrow with the other fields in this group and sort what comes back. Everything else here filters normally.
salary_currencyCurrency of the advertised pay. Compare salary figures only within one currency.
salary_currencyOperators: match, eq, in, exists
{
"where": {
"all": [
{
"field": "salary_currency",
"eq": "USD"
}
]
}
}applicants_countrequired_months_of_experienceCompetition for the role, and the experience the posting asks for.
applicants_count reads on a record; filtering on it is being improved.
applicants_countrequired_months_of_experienceOperators: gte, lte, exists
Ask for applicants_count in _source and it comes back on the record as normal. Using it as a filter answers with an error for now, rather than a set that looks right and is not, while we make the comparison reliable. Narrow with the other fields in this group and sort what comes back. Everything else here filters normally.
{
"where": {
"all": [
{
"field": "required_months_of_experience",
"gte": 24
}
]
}
}postedcreated_atupdated_atPosting dates. Take YYYY-MM-DD or a relative offset such as now-30d.
posted reads on a record; filtering on it is being improved.
postedcreated_atupdated_atOperators: gte, lte, exists
Ask for posted in _source and it comes back on the record as normal. Using it as a filter answers with an error for now, rather than a set that looks right and is not, while we make the comparison reliable. Narrow with the other fields in this group and sort what comes back. Everything else here filters normally.
Fields with a fixed set of values
These are matched against the whole stored value, so the spelling and the capitalisation both have to match. Getting either wrong is the one mistake in the grammar that raises no error: the query is valid, it costs nothing, and it returns zero, which reads as an answer about the data rather than about the query.
- Field
- seniority
- Values
AssociateDirectorEntry levelExecutiveInternshipMid-Senior levelNot Applicable
- Field
- employment_type
- Values
ContractFull-timeInternshipOtherPart-timeTemporaryVolunteer
Not every category field is a fixed set
Response shape
Successful responses use the public envelope with code, msg, and data.
Search returns IDs, and a next cursor while more pages remain:
{
"code": 200,
"msg": "ok",
"data": {
"job_ids": ["Jb71xKcAoP", "Jb0mQ4ZtRe"],
"next": "WzE3MjQwMDAwMDAsIkpiNzF4S2NBb1AiXQ"
}
}Detail returns the records, and names the IDs it could not resolve:
{
"code": 200,
"msg": "ok",
"data": {
"total": 2,
"found": 1,
"not_found": ["Jb0mQ4ZtRe"],
"results": [{
"id": "Jb71xKcAoP",
"title": "Machine Learning Platform Engineer",
"company_name": "Example Corp",
"location": "San Francisco, CA",
"seniority": "Mid-Senior level",
"posted": "2026-08-02"
}]
}
}The record itself is a page of its own: every field it can carry, what each one holds, and a whole one to read.
Access surfaces
- Surface
- REST
- How to use this capability
- POST /v1/jobs/query; POST /entity/v1/jobs/detail-by-id
- Surface
- Metix AI skill
- How to use this capability
- metix-job-search
- Surface
- MCP
- How to use this capability
- Use the MCP setup page to expose the same REST capability. Capability-specific tool names remain pending schema verification.
- Surface
- Natural-language prompt
- How to use this capability
- Find US machine-learning platform openings posted in the last 30 days, fetch the postings, and rank the hiring companies by how many they have open.
Common next step: look up the companies doing the hiring.