Skip to content
Search jobs inside your AI agent. Claude, ChatGPT, Claude Code, Codex, Gemini, Cursor and Grok
Connect
v1.0.0
OpenAPI 3.1.0

freehire API — internal reference

Base URL

All endpoints are served under https://freehire.me/api/v1. The API is read-first and open: the job, search, facet, and company endpoints need no authentication and may be called cross-origin.

Authenticated endpoints accept either the browser session cookie (set by sign-in, same-origin) or a personal API key sent as a Bearer token — see the authentication model below. Keys are created in your account, at freehire.me/my/integrations/api-keys, not through this API.

Response envelope

Single items are wrapped as { "data": ... }. Collections add pagination metadata: { "data": [...], "meta": { "total", "limit", "offset" } }. Errors are { "error": "message" } with a matching HTTP status.

{ "data": { "...": "single item" } }

{ "data": [ ... ], "meta": { "total": 4213, "limit": 20, "offset": 0 } }

{ "error": "job not found" }

Pagination

List and search endpoints page with limit (default 20, max 100) and offset (default 0). meta.total reports the total matching the current filters, so you can compute the number of pages.

Pagination is bounded on every list endpoint: offset + limit may not exceed 10000 (pagination too deep → 400). This is deep-paging protection, not a cap on the reported total — use filters to narrow rather than paging that far.

The bound is a refusal rather than a clamp, deliberately: a clamped page would answer 200 carrying rows you did not ask for, and a client walking pages would loop over the same page forever without being able to tell. A 400 means "there is no such page", which is the truth.

Errors

Errors use standard HTTP status codes: 400 (bad request / invalid value), 401 (missing or invalid credentials), 403 (authenticated but not allowed, e.g. a non-moderator), 404 (no such job, company, or owned resource), and 503 (search temporarily unavailable). The body is always { "error": ... }.

API plans

The public read endpoints answer without a key for personal and non-commercial use. Showing the postings in a product you offer to others needs an API plan, under the data licence.

Send a full-access key as Authorization: Bearer <token>, or use the OAuth access token of a signed-in MCP or CLI connection.

Authentication model

Browser clients authenticate with an HttpOnly session cookie set on sign-in (same-origin; the SPA cannot read it). Non-browser clients use a personal API key as Authorization: Bearer <token>.

Endpoints marked “Session or API key” accept either; endpoints marked “Session only” (saved searches, subscriptions) accept only the cookie, so a leaked key cannot manage credentials. “Moderator” endpoints additionally require the moderator role.

External vs internal endpoints

This reference covers endpoints that require the browser’s own session cookie — including moderator-only and browser-extension-only endpoints. None of them accept a personal API key, so an external client or agent cannot call them. For the endpoints a key (or no auth at all) can reach, see freehire.me/docs/api.

What is not here

This reference covers every endpoint you can call. A handful are deliberately left out because calling them directly is meaningless: the Gmail and calendar consent redirects (/me/gmail/connect, /me/calendar/connect, and their callbacks), which only a browser can complete; the Telegram bot webhook; the browser-tool websocket relay; the sitemap-cursor helpers behind /sitemap.xml; and the /og/*.png social-preview cards, which render an image rather than answer with JSON.

Managing API keys (POST, GET and DELETE on /me/api-keys) is not documented here, because no API client can call it. Those endpoints accept only the browser session cookie, and additionally require a freshly proven identity — a password re-entry or a sign-in-provider check — that a script has no way to supply. Create and revoke keys in your account instead, at freehire.me/my/integrations/api-keys; the plaintext token is shown exactly once, at creation, so store it then.

The /jobs/{slug}/fit endpoints are pre-rename aliases of /jobs/{slug}/match-analysis and hit the same handlers. They still work, so existing clients do not break — use the match-analysis paths in new code.

Most of this API is also a CLI. If you are writing an agent rather than an integration, freehire covers the same surface with less ceremony — search, tracking, the inbox and CV tailoring — over one API key.

Filtering jobs

Except where noted below, these parameters apply to GET /jobs/search and GET /jobs/facets. Combine any of them with full-text q.

  • Pass multiple values as a comma-separated list to OR them: skills=go,rust matches either. Repeating the param (skills=go&skills=rust) also works.
  • Add <param>_mode=and to require all selected values: skills=go,rust&skills_mode=and matches both.
  • Add <param>_exclude=<value> to exclude matches: company_type_exclude=outstaff drops outstaff jobs.
  • Different facets are ANDed together; numeric and boolean filters are ANDed too. The geography facets are the one exception — see below.
  • Geography is a single OR group: regions, countries, metros and cities widen each other instead of narrowing. regions=eu&countries=IT means "in Europe or in Italy", so it returns everything regions=eu alone would. To search one country, drop the region: countries=IT. The four name a single concept — where — so picking two places reads as "either", which is what makes regions=eu&countries=BR ("Europe or Brazil") useful. There is no AND to switch on: _mode=and does not apply to geography.
  • Use regions=none to match jobs with no resolved geography (an empty region set); it ORs with real region values and supports _exclude like any region.
  • A param no filter reads is ignored rather than refused — so old links and saved searches keep working — and comes back in meta.ignored_params, with did_you_mean when it is only the singular of a real facet. Check it: a dropped filter otherwise looks like a genuinely broad result. At most 10 are listed per response. The same report rides on /jobs/facets, /market/coverage and /companies, which answer {"data": ...} and grow a meta block only when there is something to warn about. /companies filters on its own vocabulary, so a jobs facet sent there is reported as ignored.

Facets

Every facet below supports repeat-OR, _mode=and, and _exclude as described above.

Param Filter Values
collections Collection yc, techstars, a16z-portfolio, a16z-speedrun, sequoia, accel, greylock, usv, bond, lightspeed, index-ventures, softbank-vision-fund, founders-fund, european, ai, mag7, bigtech, unicorn, fortune500, eastern-roots, indian-roots, ai-native, uk-skilled-worker-sponsor, nl-recognised-sponsor, us-h1b-sponsor, ca-lmia-employer, ie-permit-employer, dk-fast-track-company
regions Region global, north_america, latam, eu, uk, mena, africa, apac, cis, none
work_mode Work format remote, hybrid, onsite
category Specialization software_engineering, backend, frontend, fullstack, mobile, devops, sre, network_engineering, it_support, data_engineering, data_science, data_analytics, ml_ai, ai_engineering, qa, security, hardware, embedded, blockchain, architecture, design, creative, engineering_design, industrial_engineering, occupational_safety, product, project_management, healthcare, skilled_trades, retail, hospitality, logistics, education, personal_services, administration, social_services, science, manufacturing, public_safety, marketing, sales, support, business_analysis, solutions_engineering, developer_relations, technical_writing, recruiting, hr, finance, legal, operations, customer_success, other. The retired value management is still accepted and answered as role_type=people_manager
ai_archetype AI Specialization rag_app_builder, agent_builder, cloud_ml_platform_engineer, ml_trainer_researcher, fullstack_ai_engineer, devops_infra_engineer
seniority Seniority intern, junior, middle, senior, lead, staff, principal, c_level
role_type Role type people_manager — the title names a people-management role. One value only: carrying nothing means no management marker was found, NOT that the posting is individual-contributor work, so role_type_exclude means "no marker", not "IC"
skills Skills Open vocabulary — call /jobs/facets for live values
domains Industry fintech, crypto, ecommerce, gambling, gamedev, media, travel, healthcare, edtech, govtech, devtools, cybersecurity, ai, hrtech, adtech, proptech, logistics, mobility, climatetech, other
company_type Company type product, startup, outsource, outstaff, agency, inhouse, government
countries Countries Open vocabulary — call /jobs/facets for live values
cities City Open vocabulary — call /jobs/facets for live values. Recognized dictionary aliases such as München and Köln resolve to their canonical city names.
metros Metro area Open vocabulary — call /jobs/facets for live values
relocation Relocation not_supported, supported, required
employment_type Employment full_time, part_time, contract, internship, fellowship
engagement Contract terms ir35_outside, ir35_inside, w2, 1099, c2c, contract_to_hire, b2b, uop, umowa_zlecenie, umowa_o_dzielo, clt, pj, tk, gph, samozanyaty, ip, freiberuflich, arbeitnehmerueberlassung, zzp, auto_entrepreneur, portage_salarial, autonomo, monotributo — employee or own business, in each market's own term (UK IR35, US W-2/1099/C2C, Polish B2B and umowy, Brazilian CLT/PJ, Russian ТК/ГПХ/самозанятость/ИП, German, Dutch, French, Spanish and Argentine freelance statuses), as the title, the description or a source's own contract-type field states it; a posting offering several carries each, one that does not say carries none. Separate from employment_type
english_level English none, a1, a2, b1, b2, c1, c2, native
posting_language Job language Open vocabulary — call /jobs/facets for live values
reality Posting reality fresh, stale, likely-evergreen
salary_currency Currency USD, EUR, GBP, RUB
perks Perks mentioned generous_pto, four_day_week, 401k_match, parental_leave, retirement_plan, tuition_reimbursement, unlimited_pto, health_insurance, mental_health, learning_budget, home_office_stipend, sabbatical, sign_on_bonus — matched from the description; a posting without one does not mention it, which is not the same as not offering it
equity Equity salary_and_equity, equity_only, not_stated — not_stated means none was read, not that the job offers no equity
company_slug Company Open vocabulary — call /jobs/facets for live values
source_type Source type ats, company, aggregator, social
clearance_levels Clearance level none, confidential, secret, top_secret, ts_sci, public_trust, interim, other. none = no detected clearance requirement (the same postings as requires_clearance=false); other = a non-US scheme (UK SC/DV, AU NV1, NATO) or a clearance stated with no level; interim is stated beside a level, not instead of one
source Source Open vocabulary — call /jobs/facets for live values
company_size Company size 1-10, 11-50, 51-200, 201-500, 501-1000, 1000+
education_level Education level none, bachelor, master, phd
salary_period Salary period year, month, day, hour

Numeric & boolean filters

Param Filter Values
visa_sponsorship Visa sponsorship true, false
requires_clearance Security clearance required true, false
via_recruiter Posting says a recruiter publishes it for a client true — the description says so; false — every posting not marked, including recruiter posts that never say so
former_employers Hiring again: companies you have worked at and left true — signed in only, read from your experience bank; a name that does not resolve to exactly one company is left out. Anonymous, it is reported in meta.ignored_params. /jobs/facets never applies this filter, so its counts ignore it even for signed-in callers
salary_min Minimum salary integer — jobs whose minimum salary is at least this (pair with salary_currency)
salary_max Maximum salary integer — jobs whose maximum salary is at most this (pair with salary_currency)
experience_years_min Minimum experience integer — jobs requiring at least this many years
experience_years_max Maximum experience integer — jobs requiring at most this many years, the same figure experience_years_min bounds from below. Use 0 for jobs stating no prior experience is required. Either bound excludes jobs that state no requirement at all, unless experience_include_unspecified is set
experience_include_unspecified Include unstated experience true — with either experience bound, also keep the jobs that state no experience requirement (most of the catalogue). Has no effect without a bound
experience_only_seniority_unspecified Experience only without a level true — apply either experience bound only to the jobs that carry no seniority level; a job with a level passes the bound whatever it states. With seniority and seniority_include_unspecified this reads "one of these levels, or no level and within the bound". Has no effect without a bound
seniority_include_unspecified Include unstated seniority true — with seniority, also keep the jobs that carry no seniority level (most of the catalogue). Has no effect without an included seniority
posted_within_days Posted within integer — jobs whose SOURCE states a posting date in the last N days. Some boards restate that date on every crawl, so a job open for months can satisfy a narrow bound here; open_within_days is the one that cannot be rewritten from outside
open_within_days Open within integer — jobs first recorded by freehire in the last N days, i.e. how long the posting has been in the catalogue regardless of the date its source states. Independent of posted_within_days; both may be set and they narrow together
apply_questions_max Application questions integer ≥ 0 — jobs whose application form requires at most N questions beyond name, contact details and CV (3 = the "short form" filter). Jobs with no form freehire could read are excluded: unknown is not short

Recipes

  • Senior Go, remote, in the CIS region — q=go&seniority=senior&work_mode=remote&regions=cis
  • Backend roles, freshest first, in Germany — category=backend&countries=DE&sort=posted_at&order=desc
  • One country only — no region param, or it widens back out — countries=IT&employment_type=contract
  • Must use both Go and Rust — skills=go,rust&skills_mode=and
  • Exclude outstaff companies — company_type_exclude=outstaff
  • At least $100k, with visa sponsorship — salary_currency=USD&salary_min=100000&visa_sponsorship=true
  • Excluding jobs that need a security clearance — clearance_levels=none
  • Jobs stating Secret or Top Secret clearance — clearance_levels=secret,top_secret
  • Only jobs that need a security clearance — requires_clearance=true
Server:https://freehire.me/api/v1
No authentication selected
Client Libraries

Jobs

​

Public, unauthenticated reads. Jobs are returned in one wire shape (addressed by public_slug, never an internal id) shared by the list, detail, company, and search responses. Closed postings are excluded from lists and search and served only by the detail endpoint.

Apply on freehire: the employer receives your CV and answers by email.

​

Needs a verified email. The employer gets the chosen CV as a PDF, the answers, the note and your address as Reply-To. One application per vacancy: 409 once it was sent (or while it is being sent), 400 naming the first unanswered or invalid question, 403 unverified, 410 closed, 429 past the daily limit, 502 when delivery failed — applying again retries it.

Path Parameters

  • The job public_slug.

Body

application/json
  • Question id → answer; a yes/no answer is "true" or "false", a list answer one of its options.

  • The CV you tailored for THIS vacancy (cvs.tailored.id from the form). Any other CV is a 404.

  • Up to 2000 characters to the employer.

  • Send the CV file uploaded to your profile. Exactly one of resume and cv_id.

Responses

  • application/json
Request Example for post/jobs/{slug}/direct-apply
curl https://freehire.me/api/v1/jobs/platform-engineer-acme-9f3c/direct-apply \
  --request POST \
  --header 'Content-Type: application/json' \
  --cookie 'hire_token=YOUR_SECRET_TOKEN' \
  --data '{
  "resume": "true",
  "cv_id": "6f1c0c3e-8a51-4c1e-9d55-6a4c1f2b0a01",
  "answers": "{\"q1\":\"true\"}",
  "note": "Happy to talk this week."
}'
{
  "data": {
    "sent": true
  }
}

Company feedback (Collapsed)

​

Signed-in users leave a 1–5 star rating plus category and text about a company, shown under their site-wide pseudonymous persona (never a user id). Reads are public; writes are cookie-only. A reader can report a specific review, and a moderator can hide it.

Authentication (Collapsed)

​

Register/login set the session cookie and return the user. Logout clears it. me resolves the caller (cookie or API key). OAuth sign-in is a redirect flow.

Job interactions (Collapsed)

​

Per-user tracking, addressed by the job slug. All accept the session cookie or an API key and are idempotent. The response is the interaction record for that job.

Job interactions Operations

In-app assistant (Collapsed)

​

The same agent the web app and browser extension chat with, over HTTP: create a conversation, then post messages to it. All routes accept the session cookie or an API key (autopilot is cookie-only — see below) and act on a session you own; someone else’s session id answers 404, same as one that never existed. Turns run one at a time per session — a message sent while one is already running queues rather than running alongside it. preset selects which conversation this is: chat (default), profile (the experience interviewer), browse (a browsing session held from the extension’s side panel), interview (a mock-interview rehearsal), debrief (after a real one) or apply (the conversation about one automatic application) — the last three must name the vacancy via ?job=<slug>, an application you already hold. An application holds one apply conversation: asking again returns it with 200 instead of creating a second. A tailor session cannot be created here; it is minted by the CV tailoring bootstrap, which knows the CV and vacancy to bind it to.

In-app assistant Operations

Job submissions (Collapsed)

​

Any signed-in user can submit a vacancy for moderation and read their own queue. The review actions are moderator-only; approval mints a live job.

Job reports (Collapsed)

​

Any signed-in user can flag a problem with a live vacancy. Review actions are moderator-only; resolving may soft-close the reported job.

Moderator jobs (Collapsed)

​

Hand-curate a vacancy directly (moderators only). Approved submissions go through the same minting path, so a curated job is indistinguishable from an approved one.

Moderator jobs Operations

Profile & résumé (Collapsed)

​

Your career profile and stored CV, session-only (a browser feature, like saved searches). The profile is a singleton keyed by your session — no id in the path. The verdict and ATS report are read-only sub-resources computed from the profile and CV; résumé storage degrades to 501 when object storage is unconfigured.

Screening answers (Collapsed)

​

The six candidate-stated facts that repeat across ATS application forms and no CV can supply. A singleton per user — no id in the path. PUT is a partial update: a field the body omits keeps its stored value.

Screening answers Operations

Onboarding survey (Collapsed)

​

Your stored answers about your job search: how far along it is, the things in its way, and what you earn today. The onboarding tour asks only for the things in its way; the other two are kept from earlier sign-ups. A singleton per user — no id in the path. These answers describe you to us and reach no employer and no job search; what you WANT to be paid is a screening answer (desired_salary_*), not one of these. PUT is a partial update: a field the body omits keeps its stored value, and there is no way to clear one back to unstated.

Onboarding survey Operations

Saved searches & subscriptions (Collapsed)

​

Browser conveniences, session-only. A saved search stores a canonical filter query string; a subscription turns one into a recurring digest (Telegram, email, or your own webhook). Each operation is owner-scoped — a non-owned id is a 404.

Push notifications & alerts (Collapsed)

​

The mobile app’s device push tokens, and the in-app notification center — a durable, channel-independent record of every delivered digest, reminder and nudge. All cookie-only.

Account, plan & extension (Collapsed)

​

The rest of the account surface: the password, deleting the account, the plan and what it allows today, and the two endpoints the browser extension runs on. Password and deletion are session-only — an API key must not be able to change or destroy the credential it would outlive.

Votes, notifications & discussions (Collapsed)

​

The lighter per-user surfaces: a vote on a job or company, the account-level notification rule (gates the saved-job apply reminder and both lifecycle nudges), and the public discussion threads. Votes and notification settings take a key; posting to a thread is browser-owned.

Market pulse (Collapsed)

​

Your own profile skills’ demand trend, joined against the weekly market snapshot. Cookie-only, unlike the aggregate insights above — this reads your saved profile.

Market pulse Operations

Employee referrals (Collapsed)

​

Two sides of the same marketplace: an employee offers to refer into the company they work at (proof required, moderated), and a candidate asks one of that company's approved referrers for an intro. A candidate never sees who the referrer is, and a referrer sees the candidate's CV only through a short-lived view. Moderation of offers is moderator-gated.

Talent Network (Collapsed)

​

A public, anonymised catalogue of candidates who opted into being found. Membership is a single toggle on users, distinct from user_profiles; a member is addressed by a minted handle, never by their account id or username. Every string a public response carries is a value a dictionary resolved, a number or a date — never text copied from a CV — so no name, employer, institution or free-text field appears anywhere in it.

Talent Network Operations

CV builder & tailoring (Collapsed)

​

Your CVs, and the tailoring flow that reframes one toward a single vacancy. Authoring is browser-owned — create, update and delete are session-only — while reading a CV, patching it, and rendering the PDF also accept an API key, because that is the half a tailoring agent drives. Every CV is owner-scoped: an id you do not own is a 404, not a 403.

Photo (Collapsed)

​

The one image the CV templates that print a portrait compose in. Cookie-only throughout — the image is PII, so there is no key-authenticated or public path.

Experience bank (Collapsed)

​

The durable record a CV is built from: employments, and the evidence atoms under them. Every atom carries who asserted it — you, or a model that inferred it — and only your own assertions may be written into a CV. Reading accepts an API key; editing is session-only.