MCP server

Connect AI agents and MCP clients to JobsPipe — search live job postings over the Model Context Protocol, signing in with OAuth or an API key.

JobsPipe runs a Model Context Protocol server so AI agents and MCP-capable clients (Claude, ChatGPT, Cursor, and custom MCP hosts) can search live job postings directly, without you writing any HTTP code.

Add JobsPipe to your AI tool

ClaudeCursorVS CodeVS Code InsidersClaude CodeChatGPTCodexWindsurf

Claude, Cursor and VS Code install in one click. The others open their setup steps below.

The server speaks MCP over Streamable HTTP at:

https://mcp.jobspipe.dev/mcp

How it works

The MCP server is a thin layer over the JobsPipe API. Every tool call that needs data is forwarded to the API under your account, so:

  • Results, sources, and freshness are identical to the REST API.
  • Usage counts against your plan, and your plan's quota and rate limits apply.

There are two ways to authenticate, and both land on the same account.

OAuth sign-in (default)

Clients that support MCP authorization — Claude and ChatGPT connectors, Claude Code, and other hosts that implement OAuth discovery — only need the URL. When a client connects without credentials, the server answers 401 with a WWW-Authenticate header pointing at its protected-resource metadata:

WWW-Authenticate: Bearer realm="jobspipe", resource_metadata="https://mcp.jobspipe.dev/.well-known/oauth-protected-resource"

The client follows that link to the authorization server at https://api.jobspipe.dev (metadata at /.well-known/oauth-authorization-server), registers itself, and sends you through a JobsPipe sign-in and consent screen. It uses the authorization-code flow with PKCE (S256) and refresh tokens, and then sends the access token as Authorization: Bearer <token> on every request. No key needs to be copied anywhere.

The consent screen names the app, shows the redirect URLs it registered, and lists what it is asking for. Approving lets that app search jobs and scan tech stacks as you — every call counts against your plan, the same as your own — and create or change your saved signals. It cannot see your password, change your billing, or reach anything outside your own account. Approval belongs to the account you are signed in to at that moment, and the app is only ever sent back to a redirect URL it registered.

If a token expires or is revoked, the server answers 401 with Access token is invalid or expired. Reconnect to continue. — reconnect the client to sign in again.

API key header

For clients without OAuth support, scripts, and CI, use the same API key you use for the REST API — a key that starts with jp_live_. Create or copy one from your dashboard and send it in either header:

Authorization: Bearer jp_live_xxxxxxxxxxxxxxxxxxxxxxxx
x-api-key: jp_live_xxxxxxxxxxxxxxxxxxxxxxxx

x-api-key is read only when there is no Authorization: Bearer header. A bearer value that does not start with jp_live_ is treated as an OAuth access token.

A connection with no credentials at all is rejected with 401 Unauthorized. A jp_live_ key is not validated when you connect, only when a tool reaches the API — see Limits and errors.

Install it

Pick your client. Every route lands on the same server and the same account.

Claude Code

claude mcp add --transport http jobspipe https://mcp.jobspipe.dev/mcp --scope user

Then run /mcp inside Claude Code and choose Authenticate for jobspipe — a browser opens, you sign in to JobsPipe, and the tools appear. Drop --scope user to add it to the current project only, or use --scope project to write a .mcp.json your team can commit.

To use an API key instead of signing in, pass it as a header (and skip the /mcp authenticate step):

claude mcp add --transport http jobspipe https://mcp.jobspipe.dev/mcp \
  --scope user --header "Authorization: Bearer jp_live_xxxxxxxxxxxxxxxxxxxxxxxx"

A committed .mcp.json reads the key from the environment, so no secret is checked in:

{
  "mcpServers": {
    "jobspipe": {
      "type": "http",
      "url": "https://mcp.jobspipe.dev/mcp",
      "headers": {
        "Authorization": "Bearer ${JOBSPIPE_API_KEY}"
      }
    }
  }
}

type is required whenever an entry has a url. Without it the entry is read as a local command and the connection fails.

Claude (web, desktop and mobile)

Add to Claude

  1. Open Settings → Connectors and click Add custom connector.
  2. Paste https://mcp.jobspipe.dev/mcp as the remote MCP server URL.
  3. Click Connect. Claude sends you to a JobsPipe sign-in and consent screen, and that is the whole setup — there is no key to paste and no client ID to fill in under Advanced settings.

On Team and Enterprise plans an owner adds the connector once under Organization settings → Connectors, and each member then connects their own JobsPipe account from Settings → Connectors.

ChatGPT

ChatGPT connects to JobsPipe as a plugin backed by the MCP server. It cannot send an API key, so sign-in is the only route, and it needs Developer mode once.

  1. Open Settings → Security and login and turn on Developer mode. It has to stay on while the plugin is installed.
  2. Open Settings → Plugins and click the + to create a plugin. Name it JobsPipe and enter https://mcp.jobspipe.dev/mcp as the MCP server URL. Leave Authentication on OAuth.
  3. Click Create, then Connect. ChatGPT registers itself, opens a JobsPipe sign-in and consent screen, and the tools become available in a chat.

Two things that look like the plugin but are not: a JobsPipe entry installed from Browse plugins that shows curl commands and asks for an API key is a skill document, not this server, and ChatGPT will answer that it has "no usable API key"; remove it and create the plugin above. And if ChatGPT never shows a sign-in, Developer mode is off.

ChatGPT cannot send an API key to a connector, so OAuth is the only route there. The server also answers the search and fetch tools that ChatGPT's deep research and company-knowledge features require, so JobsPipe can be used as a research source and every job it cites links back to the original posting.

Cursor

Add jobspipe MCP server to Cursor

Or add it by hand — ~/.cursor/mcp.json for every project, .cursor/mcp.json inside one project:

{
  "mcpServers": {
    "jobspipe": {
      "url": "https://mcp.jobspipe.dev/mcp"
    }
  }
}
{
  "mcpServers": {
    "jobspipe": {
      "url": "https://mcp.jobspipe.dev/mcp",
      "headers": {
        "Authorization": "Bearer ${env:JOBSPIPE_API_KEY}"
      }
    }
  }
}

Cursor has no type field for remote servers — a url is enough. Open Settings → MCP to confirm jobspipe is connected, and to sign in if you left headers out.

Codex

Add the server from the Codex CLI, then sign in:

codex mcp add jobspipe --url https://mcp.jobspipe.dev/mcp
codex mcp login jobspipe

codex mcp login opens a browser, you sign in to JobsPipe, and the tools appear in the next session.

To use an API key instead, put the server in ~/.codex/config.toml and let Codex read the key from the environment, so it is never written to the file:

[mcp_servers.jobspipe]
url = "https://mcp.jobspipe.dev/mcp"
bearer_token_env_var = "JOBSPIPE_API_KEY"

Export JOBSPIPE_API_KEY=jp_live_... in the shell that starts Codex. Codex sends it as an Authorization: Bearer header; skip codex mcp login in that case.

VS Code and GitHub Copilot

Install in VS Code Install in VS Code Insiders

Or add it to .vscode/mcp.json — note the wrapper key is servers, not mcpServers:

{
  "servers": {
    "jobspipe": {
      "type": "http",
      "url": "https://mcp.jobspipe.dev/mcp"
    }
  }
}

To send an API key instead of signing in, let VS Code prompt for it once and keep it out of the file:

{
  "servers": {
    "jobspipe": {
      "type": "http",
      "url": "https://mcp.jobspipe.dev/mcp",
      "headers": { "Authorization": "Bearer ${input:jobspipe-api-key}" }
    }
  },
  "inputs": [
    {
      "type": "promptString",
      "id": "jobspipe-api-key",
      "description": "JobsPipe API key",
      "password": true
    }
  ]
}

Windsurf

In ~/.codeium/windsurf/mcp_config.json, remote servers use serverUrl:

{
  "mcpServers": {
    "jobspipe": {
      "serverUrl": "https://mcp.jobspipe.dev/mcp",
      "headers": {
        "Authorization": "Bearer ${env:JOBSPIPE_API_KEY}"
      }
    }
  }
}

Any other MCP client

Point it at https://mcp.jobspipe.dev/mcp over Streamable HTTP. Clients that implement MCP authorization need nothing else; the rest send the key as a header.

{
  "mcpServers": {
    "jobspipe": {
      "type": "http",
      "url": "https://mcp.jobspipe.dev/mcp",
      "headers": {
        "Authorization": "Bearer jp_live_xxxxxxxxxxxxxxxxxxxxxxxx"
      }
    }
  }
}

Once connected, the tools below appear automatically and the agent can call them.

Tools

ToolDescription
searchFind postings from a plain-language request, best match first, with results to cite: an id, a title and a link each.
fetchRead one posting in full by the id a search result carried.
search_jobsSearch live job postings from 30+ sources, normalized into one schema, with every filter.
search_hiring_companiesThe companies behind a job search, one row per company with its matching jobs, nested by corporate group.
suggest_companiesRanked candidate companies for the start of a name, with key, domain, open postings and whether the domain is verified. Free.
resolve_companyLook up one company by its domain or name: its record and its key. Never guesses; an ambiguous or unconfirmed name answers up to 5 candidates.
get_company_profileA company's page by its key: funding, acquisitions, officers, offices, similar companies, hiring trends, technology changes and its group.
create_signalSave a search and be told when something new matches it, by email, Slack or a signed webhook.
list_signalsList the signals on your account, with their filters, destinations and when each was last checked.
update_signalRename, pause, resume or change the search of one of your signals. Free.
delete_signalDelete one of your signals for good, after an explicit confirm: true. Free.
detect_company_tech_stackDetect the technologies a company serves on its domain, with confidence scores.
search_companies_by_technologyFind companies whose own job postings show they use given technologies, with graded evidence per technology.
get_company_tech_stackEvery technology one company's job postings name, graded by evidence tier, with counts and dates. For an ambiguous name, run suggest_companies first.
resolve_technologiesTurn a list of technology or product names, or vendor domains, into the slugs search_companies_by_technology takes, with how each matched and how many companies have evidence for it. Free.
search_documentationSearch these docs and get back the matching sections with their text and a link.
list_pricing_plansList JobsPipe plans with monthly price, job quota, and max results per call.
get_account_infoShow which account the connection is signed in to, its plan, and the credits used and left this month.
give_feedbackTell us what was wrong or missing: a stale posting, a wrong field, a search that missed, a confusing tool. Free; a person reads every submission.
buy_credits_with_agent_walletBuy one-time extra credits for a paid package from an agent wallet, with no checkout page. Offered only where agent-wallet payments are enabled.
upgrade_planGet a Stripe checkout link for a bigger package, sized to your use unless you name one. Nothing is charged until you pay on that page.

search, fetch, search_jobs, search_hiring_companies, detect_company_tech_stack and the two company technology tools call the API and count against your plan. search_hiring_companies and search_companies_by_technology cost 1 credit per company returned, once per calendar month; get_company_tech_stack and detect_company_tech_stack cost 1 credit when they find something. resolve_company costs 1 credit when it returns a record, and get_company_profile 1 credit per company page, free when that company was already paid for this calendar month. A call that returns nothing, or fails, costs nothing. A job is charged once per calendar month, so a fetch of a posting that search already returned is free. search_documentation and list_pricing_plans are answered by the MCP server itself and use no credits. suggest_companies and resolve_technologies are free too and share one limit of 5 calls a second. get_account_info, create_signal, list_signals, update_signal, delete_signal, give_feedback and upgrade_plan read or write your account and use no credits either; a signal costs no job credits to evaluate. buy_credits_with_agent_wallet uses no credits either; you pay only through the link it returns.

Every tool that uses credits also says how many are left, and warns when you are running low: see low_balance.

search and fetch

search and fetch are the pair that research and connector features look for by name — ChatGPT's deep research and company knowledge among them — so a JobsPipe search can be cited in a report like any other source. They are a simpler view of the same corpus search_jobs serves. search runs on agentic search: it plans structured searches from the request, checks hard rules on each posting and returns the postings that fit best first, up to 25. If agentic search cannot answer, search falls back to matching the request against job titles, so a call never fails for that reason.

search takes a query and, optionally, country_code (ISO 3166-1 alpha-2), city, remote, posted_within_days and limit (1 to 50, default 10; agentic search returns at most 25). It answers a results array of { id, title, url }, where url is the posting itself and is what gets cited, and a usage block saying what the call cost.

{
  "results": [
    {
      "id": "b1f3c0d2e4a5",
      "title": "Senior Data Engineer - Acme Corp (Berlin, Germany)",
      "url": "https://example.com/jobs/b1f3c0d2e4a5"
    }
  ],
  "usage": { "credits_charged": 1, "jobs_already_paid": 0, "credits_remaining": 24382 }
}

usage says what that search cost: credits_charged is the credits it used, jobs_already_paid is how many of the postings were free because your account already paid for them this calendar month, and credits_remaining is what the account can still spend afterwards. It is absent on an account that is not billed per job.

fetch takes the id of a result, and optionally include_technologies (see search_jobs), and answers that one posting as { id, title, text, url, metadata }. text is the posting as readable prose — role, employer, location, work arrangement, employment type, seniority, salary, dates, skills and the posting body — and metadata carries the same facts as individual string fields, plus credits_charged and jobs_already_paid for what reading it cost and credits_remaining for what is left. An id that no longer resolves comes back as a tool error naming it; ids stop resolving once a posting closes.

For anything these two do not cover — salary floors, skills, sources, visa stance, language, industry or occupation codes, paging past 50 — use search_jobs below.

search_jobs

Every filter is optional and combined with AND. Array filters ending in _or match any of their values.

Text and company

ParameterTypeDescription
job_title_orstring[]Match jobs whose title contains any of these phrases.
job_title_notstring[]Exclude jobs whose title contains any of these phrases.
description_orstring[]Match jobs whose description contains any of these phrases.
company_name_orstring[]Match jobs from any of these exact company names.
company_name_partial_match_orstring[]Match jobs whose company name contains any of these, e.g. ["acme"] finds "Acme Corp" and "Acme Ltd".
employer_type_orstring[]Keep only these kinds of employer: employer (the company itself), agency, broker.
employer_type_notstring[]Drop these kinds of employer. ["agency","broker"] keeps only jobs posted by the hiring company.
min_employee_countnumberCompanies with at least this many employees. Jobs whose company size is unknown are dropped unless include_unknown_size is true.
max_employee_countnumberCompanies with at most this many employees. Same rule for unknown sizes.
include_unknown_sizebooleanKeep jobs whose company size is unknown when filtering by employee count. Most postings carry no size, so a size filter without this returns far fewer results.
min_revenue_usdnumberCompanies whose estimated annual revenue, in US dollars, is at least this amount (5000000 = $5M).
max_revenue_usdnumberCompanies whose estimated annual revenue is at most this amount. Companies with no revenue on record are dropped unless include_unknown contains "company_revenue".
lei_orstring[]Legal Entity Identifiers (ISO 17442). Matches jobs at a company with one of these LEIs or at any company in the corporate group the LEI heads. 1 to 50, case ignored.
company_key_orstring[]Company ids (the key of a search_hiring_companies row): only jobs at exactly these companies, never a longer name that starts with the same words. 1 to 50.
company_hq_country_orstring[]Companies headquartered in these countries (ISO 3166-1 alpha-2), e.g. ["DE"]: where the company is based, not where the job is.
company_hq_country_notstring[]Only companies headquartered outside these countries; companies with no known headquarters never match.
company_technology_slug_orstring[]Only jobs at companies whose own postings show they use any of these technologies, e.g. ["snowflake","dbt"]; at most 10. Offered where the server has technology filters turned on.

Location

ParameterTypeDescription
job_country_code_orstring[]ISO country codes to include, e.g. ["US","GB"].
job_country_code_notstring[]ISO country codes to exclude, e.g. ["IN"].
job_location_orstring[]City or region contains any of these, e.g. ["Seattle","WA"]. Terms of three characters or fewer match whole values (WA is Washington, never Iowa). Combine with job_country_code_or to disambiguate same-named cities.
city_orstring[]Whole city names, e.g. ["London","Munich"]. Case-insensitive; a metro wrapper is ignored (Greater London is London), and spellings and diacritics are normalised for cities we know (München finds Munich). The precise filter for a city by name. Combine with job_country_code_or to disambiguate same-named cities.
region_orstring[]US states and Canadian provinces as ISO 3166-2 codes, e.g. ["US-NY","CA-ON"]. More precise than job_location_or for a state or province.
metro_code_orstring[]US CBSA metro codes, e.g. "35620" (New York). Non-US jobs never match.
remotebooleantrue returns remote-only, false excludes remote.
work_arrangement_orstring[]remote, hybrid or onsite — finer than remote, which reads false for hybrid and onsite alike. Jobs with an unknown arrangement never match.

Source

ParameterTypeDescription
source_orstring[]Match any of these job boards or career sites, e.g. ["linkedin","greenhouse"]. Case, spaces and punctuation are ignored; yc is an alias for ycombinator.
source_notstring[]Exclude sources. ["indeed","linkedin"] drops the two largest boards; pin the ATS list in source_or for ATS-only.

Role and classification

ParameterTypeDescription
employment_type_orstring[]full-time, part-time, contract, temporary, internship.
include_unlabeled_employment_typebooleanAlso return jobs with no employment type.
job_seniority_orstring[]Seniority levels to include.
include_unlabeled_senioritybooleanAlso return jobs with no seniority (most postings state none).
skills_orstring[]Skill slugs, e.g. ["python","kubernetes"].
esco_skill_id_orstring[]ESCO skill concept IDs (exact match).
occupation_code_orstring[]ISCO-08 codes; 4 digits exact ("2512" Software Developers), 1-3 digits match as prefixes.
isic_division_orstring[]ISIC Rev.4 employer industry divisions, 2 digits ("62" Computer programming).

Language

ParameterTypeDescription
language_orstring[]Languages the posting is written in, as lowercase ISO 639-1 codes, e.g. ["en"]. The language of the text, not the country: many Swedish and German postings are in English. Unlabelled postings never match; add include_unknown: ["language"] to keep them.
language_notstring[]Exclude postings written in these languages, e.g. ["fr"]. Unlabelled postings are kept.

Pay, perks and posting signals

ParameterTypeDescription
min_salary_usdnumberPosted salary (top of range, annualized USD) reaches this amount. Jobs without a posted salary never match.
benefits_orstring[]Benefit slugs, e.g. ["401k","health insurance"]. Structured source data only, so coverage is partial.
visa_sponsorship_orstring[]offers, no or citizenship_required, parsed from the posting text. Jobs that say nothing never match.
has_recruiter_emailbooleantrue for only jobs with a parsed recruiter email, false for only jobs without one.
max_applicant_countnumberAt most this many applicants. Only LinkedIn exposes counts, so jobs without one are dropped.
max_ghost_scorenumberExclude jobs whose ghost-likelihood score (0-100) exceeds this. Unscored jobs pass.

Dates, paging and output

ParameterTypeDescription
posted_at_gtestringOnly postings on or after this date (YYYY-MM-DD).
posted_at_ltestringOnly postings on or before this date (YYYY-MM-DD).
posted_at_max_age_daysnumberOnly postings newer than this many days.
last_verified_max_age_daysnumberOnly postings we last confirmed live on their source within this many days.
statusstringactive (the default), closed, or any. Closed postings keep their data and are how a past hiring question is answered.
include_unknownstring[]Field names whose unlabelled jobs that field's filter should keep rather than drop, e.g. ["language"].
limitnumberRows to return. Defaults to 25 and is clamped to your plan's max results per call (25 on Free).
offsetnumberRows to skip, for paging.
cursorstringContinue an earlier search from metadata.next_cursor.
detailstringcompact (the default) or full. The tool's own argument, not a search filter.
include_total_resultsbooleanPopulate metadata.total_results (slightly slower).
blur_company_databooleanDeprecated and ignored. Preview mode has been removed; every search returns the full record and is billed per job.

About 17% of postings carry an arrangement, so work_arrangement_or returns a real but partial slice and silently drops the rest. Use remote for breadth, work_arrangement_or when hybrid and onsite must be told apart.

A typical agent call:

{
  "job_title_or": ["data engineer"],
  "remote": true,
  "posted_at_max_age_days": 14,
  "limit": 25,
  "include_total_results": true
}

The response mirrors the REST API: a metadata block (with total_results when requested) and a data array of normalized postings — title, company, location, country, salary range, seniority, posting date, and the apply URL. Each posting also carries sources (every board it was seen on, not just the first), last_seen_at and verified_at (when a recheck last confirmed the posting was live, and when we last looked at all), and is_manager and job_function where they are known — is_manager is what separates a principal individual contributor from an actual director, since seniority files both under "director". See the job schema for the full shape and for how much of the corpus carries each field.

How much of each posting you get. Rows come back compact by default: the fields a list of results is read for, with the description cut to a snippet and anything empty left out. A compact row says description_truncated: true and description_chars when it cut one, so you know there is more to read. Read one posting in full with fetch, or pass detail: "full" to get every field of every row exactly as the REST API answers it.

Paging. Page with cursor: take metadata.next_cursor from a response and send it back as cursor on the next call, unchanged, with the same filters. Stop when a page comes back with no next_cursor. offset still works and is simpler for a handful of pages, but a cursor is steadier on a corpus that keeps growing and is the only way past the offset ceiling. Because limit is clamped silently to your plan's page size, a page shorter than the limit you asked for does not mean you reached the end.

What a call cost. metadata.credits_charged is what the call used and metadata.jobs_already_paid is how many rows were free because your account already paid for them this calendar month. Both are absent on an account that is not billed per job.

Technologies. Pass include_technologies: true to add each posting's graded technologies (in a compact row, e.g. "Snowflake (required)"). It is off by default and costs 1 extra credit per returned job that names at least one technology, once per job per calendar month; metadata.technologies_credits_charged reports that part of the cost. fetch takes the same optional include_technologies argument and adds a Technologies: line and a technologies metadata field to the document.

create_signal and list_signals

A signal is a saved search that tells you when something new matches it, so an agent does not have to re-run the same search on a timer. Matches are keyed on the first time a posting entered the corpus, so a repost or a backfill does not fire again, and evaluating a signal costs no job credits.

create_signal takes:

ParameterTypeDescription
namestringA short label for this signal.
filtersobjectThe search that defines a match, in the shape search_jobs takes, minus paging and anything that decides what counts as new — the signal keeps its own watermark.
destinationsobject[]Where matches go: { kind: "email" | "slack" | "webhook", target, cadence: "instant" | "daily" }. Email is available on every plan.
modestringjobs fires on each newly matching posting; companies (the default) fires the first time a company matches; technology_adoption (beta, offered only where it is enabled) fires the first time an established company starts hiring for a technology.
intentstringFree text describing what you are watching for.
idempotency_keystring8 to 64 characters. Your own key for this signal. Send the same key on a retry and the signal already saved is replayed instead of a second one being created.

Run the same filters through search_jobs first to see what they return before saving them.

With mode: "technology_adoption", filters takes only technology_slug_or (required, 1 to 10 slugs), company_country_code_or, tier_min (confirmed, the default, or likely) and min_prior_jobs (postings the company published before its first posting naming the technology, default 5), and no intent; job filters and unknown slugs are refused. Offered only on accounts where the beta is enabled. It fires once per company and technology. See Signals → Technology adoption for what counts as adopting.

Both tools answer the same shape — create_signal one signal, list_signals an array of signals:

{
  "signal": {
    "id": "9f1c2f1e-...",
    "name": "Fintech hiring in Berlin",
    "mode": "jobs",
    "enabled": true,
    "filters": { "job_title_or": ["backend engineer"], "job_country_code_or": ["DE"] },
    "intent": "Berlin fintechs starting to hire backend engineers",
    "grade_leads": false,
    "created_at": "2026-09-22T10:00:00.000Z",
    "last_evaluated_at": null,
    "last_error": null,
    "consecutive_failures": 0,
    "destinations": [
      {
        "id": "7a2b3c4d-...",
        "kind": "webhook",
        "target": "https://example.com/hooks/jobspipe",
        "cadence": "instant",
        "enabled": true,
        "signing_secret": "whsec_..."
      }
    ]
  }
}

A webhook destination's signing_secret is returned once, when the signal is created — store it then, because a later list_signals does not include it. See Signals for how deliveries are signed and retried.

update_signal

ParameterTypeDescription
signal_idstringRequired. The signal's id, from create_signal or list_signals.
namestringA new label, 1 to 120 characters.
enabledbooleanfalse pauses the signal: nothing is evaluated or delivered until it is true again.
filtersobjectReplaces the saved search as a whole, in the shape create_signal takes. Send every filter the signal should keep, not only the one that changes.
intentstring | nullNew free text describing what you are watching for; null clears it.

Send signal_id and at least one of the others; anything left out keeps its saved value, and a call with nothing to change is refused before it reaches your account. Sending the same change twice leaves the signal the same. Returns the updated signal in the same shape as create_signal. Free.

If filters are refused, the error's next_step lists the filter keys a signal takes. An id that is not one of your signals answers Signal not found, with a next_step to call list_signals; another account's signals are never visible or changeable.

delete_signal

ParameterTypeDescription
signal_idstringRequired. The signal's id, from create_signal or list_signals.
confirmbooleanRequired as true. Without it the call is refused and nothing is deleted.

Deleting is permanent: the signal stops being evaluated, its destinations stop receiving matches, and it cannot be restored; create it again to resume. To stop a signal for a while, use update_signal with enabled: false instead. Without confirm: true the call answers an error whose next_step asks for confirmation and suggests list_signals when unsure which signal is meant. Returns only { "id", "name", "deleted": true }. An id that is not one of your signals answers Signal not found and deletes nothing. Free.

detect_company_tech_stack

ParameterTypeDescription
domainstringRequired. Domain to scan, e.g. "stripe.com". URLs and www. are normalized.
modestringauto (default) tries a fast HTTP fetch, then a headless render if results are thin. html or render forces one strategy.

Returns domain, scanned_at, http_status, and a detected array — each entry with slug, name, categories, confidence, version, website, saas, and oss. Results are cached for 14 days.

This is what a company's website runs. What it hires for, from its own job postings, is get_company_tech_stack.

search_hiring_companies

Takes every search_jobs filter except detail, include_technologies and blur_company_data, which apply only to jobs (offset here is a whole number from 0 to 10,000), plus order_by (jobs, the default; last_posted; employees) parent (a group's lei from a previous answer, to list all of that group's companies) and known_company_only (true keeps only companies with a verified company profile, leaving out job boards and aggregators that post under their own name; off by default). Returns the response of companies hiring for a search: metadata and a data tree of companies, each with its matching jobs, jobs_in_subtree, last_posted, countries, website, logo, employees, industry, revenue and headquarters country and city, nested by corporate group under children. Filter by where companies are based with company_hq_country_or; with no filters it lists every hiring company. Group rows without jobs of their own have a null key and are free; staffing agencies and job boards are separate top-level rows. To read a row's jobs, call search_jobs with company_name_or for a company or lei_or for a group.

suggest_companies

ParameterTypeDescription
querystringRequired. The start of a company name, 2 to 100 characters.
limitintegerHow many candidates, 1 to 10; 10 by default.

Returns the response of company suggest: query and candidates, best first, each with key, name, domain, jobs, hq_country and verified. An exact name ranks first, then companies whose domain is verified, then by open postings. Costs nothing and has its own limit of 5 calls a second. Use it when a name is ambiguous, or when get_company_tech_stack answers company_not_found: that error also carries the same candidates. To read a candidate's jobs, call search_jobs with company_key_or or company_name_or; for its hiring numbers, search_hiring_companies; for its full page (funding, leadership, offices, peers, hiring trends against the market, technology changes, corporate group), pass its key to get_company_profile; a verified candidate's domain resolves with resolve_company and is the company for get_company_tech_stack.

resolve_company

ParameterTypeDescription
keystringRequired. The company's domain (stripe.com) or its name (Stripe), 1 to 253 characters. A domain is the surer match.

Returns the company lookup record: name, domain, url, logo, employee_count, employee_count_min, linkedin_url, description, location, founded, intelligence and key. The key opens get_company_profile and is company_key_or on search_jobs; it is null when no postings are behind the record, and then a next_step says to find a key with suggest_companies. Costs 1 credit when the record is returned.

It never guesses. A name answers only when exactly one company has that exact name and its domain is confirmed; otherwise the call fails, for free, with company_not_found, up to 5 candidates (each with key, name, domain, jobs, hq_country and verified) and a next_step saying what to do with them: pass a candidate's key to get_company_profile, use its name in search_jobs, or resolve a verified candidate's domain.

get_company_profile

ParameterTypeDescription
keystringRequired. A company's key (the digits a search_hiring_companies row, a suggest_companies candidate or resolve_company carries), or g-<LEI> for a whole corporate group. A domain or a name is refused before any call, with a next_step pointing at resolve_company and suggest_companies.
sectionsstring[]Which parts of the page: header, facts, offices, peers, trends, tech_changes, subsidiaries. ["header","facts"] by default; header is always included. Ask only for what the question needs.

Returns the company page for the sections asked: key, found, the sections, metadata (including credits_charged) and truncated. Costs 1 credit; free when the company was already paid for this calendar month, and free when it is not found (a 404 with a next_step).

So a full page fits an assistant's context (about 12 KB with every section), long lists are shortened: 5 funding rounds, acquisitions, officers, offices, similar companies and listed brands; 8 group entities and 5 group companies; 10 new and changing technologies; the newest 8 weeks of each weekly trend; 5 job functions and countries; 3 new and quiet locations and leadership roles. Method notes (caveat), logo URLs and place ids are left out, along with shares a change field already states. truncated lists the paths of the lists that were cut, e.g. facts.funding_rounds; totals such as funding_round_count still count everything. The REST endpoint returns the page in full.

search_companies_by_technology

Available on every plan. Takes the filters of company search by technology: company_technology_slug_or, company_technology_slug_and, company_technology_slug_not, expand_technology_slugs, tier_min, min_num_jobs_found, company_country_code_or, company_domain_or, company_name_or, min_employee_count, max_employee_count, order_by, limit, cursor and include_total_results, and returns the same metadata and data. Each company's technologies_found carries the evidence: tier (confirmed, likely, mentioned), posting counts, and the first and last date a posting named it.

The tool asks for the compact shape unless told otherwise: detail is "compact" by default, which leaves out each company's whole technology list and the category ranking so a page of 500 companies is about 250 KB; detail: "full" is every field the API answers with. include_sample_jobs set to true adds sample_jobs to each evidence entry, up to 3 of the newest postings from the last 180 days that named the technology, each with its id, title, url and posted_at, as proof an assistant can cite or fetch. It costs no extra credits. To turn product names into slugs first, call resolve_technologies.

resolve_technologies

ParameterTypeDescription
namesstring[]Required. Technology or product names as you have them, or vendor domains such as okta.com. Up to 500 per call.

Returns the response of resolve names to slugs: one row per name in the order sent, with slug, technology_name, match (exact, alias, domain, prefix or none) and the companies and jobs with evidence. A prefix match means the name only starts with the technology: check it before searching. none means the name is not tracked, which is no signal, not proof nobody uses it. Free, so paste the whole list.

get_company_tech_stack

ParameterTypeDescription
companystringRequired. A domain ("stripe.com") or a company name.
tier_minstringconfirmed, likely (default) or mentioned.
kindstringComma-separated kinds to keep, e.g. "product,certification".

Returns the response of company tech stack: as_of, company, num_jobs, num_technologies and data.

search_documentation

ParameterTypeDescription
querystringRequired, at least 2 characters. What to look up, e.g. "filter by salary" or "webhook signature".
limitnumberMaximum sections to return, an integer from 1 to 20. Default 5.

Returns the query and a results array of sections, each with title, heading, url, excerpt, and score.

list_pricing_plans

Takes no parameters. Returns currency (USD), an upgrade line, and a plans array with each plan's name, monthlyPriceUsd, monthlyJobs, maxResultsPerRequest, and requestsPerSecond. Each package also carries a checkout_url that opens it on the billing page; upgrade_plan returns a one-click Stripe link instead.

get_account_info

Takes no parameters. Returns the account the connection is signed in to: user_id, email, name, auth_type (oauth or api_key), plan, the month the figures cover, monthly_credits, one_time_credits (Free only), credits_used, credits_remaining, extra_credits, max_results_per_request and requests_per_second. It costs no credits.

give_feedback

Sends us a report about the data or the tools. Your assistant is told to use it when a posting looks closed or wrong, a field is off, a search misses what should exist, or a tool is confusing; you can also ask it to. It costs no credits and does not count against the search rate limit. A person reads every submission; up to 10 a minute and 100 a day per account.

ParameterTypeDescription
categorystringdata_quality, missing_data, search_results, tool_usability, pricing or other.
messagestringWhat was wrong, missing or confusing, up to 4,000 characters.
job_idsstring[]The postings the report is about, by the id a search or fetch returned. Up to 50.
toolstringThe tool that was being used, e.g. search_jobs or fetch.
queryobjectThe exact search input that was run, so a miss can be replayed.
agentstringWhich client or model is sending the report.
reasonstringOptional, for a report about postings: wrong_company, wrong_location, wrong_salary, wrong_details, closed, duplicate or other.
{
  "id": "f0c3a1d2-6b7e-4a1f-9d0e-5c2b8a7e4f10",
  "received_at": "2026-10-06T07:00:00.000Z",
  "note": "Thanks. A person reads every submission."
}

The same report can be sent over REST as POST /v1/feedback with the same fields.

upgrade_plan

Returns a Stripe link where you buy a package, or confirm a move to a larger one. Your assistant is told to use it when you ask for more credits, when a tool answers that you are out of credits, or when a result carries low_balance, and to show you the link rather than claim anything was bought. The call itself charges nothing; the new monthly allowance applies within a minute of paying.

ParameterTypeDescription
packagestringbuilder, growth, scale or business.
creditsnumberMonthly credits wanted instead of a package name; the smallest package that covers them is chosen.

With neither, it picks the package that covers 1.5x what you used this month, and never the package you already hold.

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_live_a1B2c3",
  "flow": "checkout",
  "package": "growth",
  "credits": 100000,
  "billing_interval": "month",
  "price_usd": 149,
  "note": "Open this link to pay with Stripe; the new allowance is live within a minute."
}

flow is checkout for a first package and portal when the link confirms a move to a larger package than the one you hold. The tool never changes your plan itself. A smaller package is refused with downgrade_requires_dashboard and a billing_url: downgrades are made on the billing page. More than 500,000 credits a month is an Enterprise plan: the tool answers an error with contact_url.

buy_credits_with_agent_wallet

Offered only on servers where agent-wallet payments are enabled. Buys one-time extra credits for a paid package, paid from an agent wallet over the Machine Payments Protocol (a Stripe Shared Payment Token), with no checkout page. Your assistant is told to use it only when you have approved spending that amount. Free accounts cannot buy extra credits: use upgrade_plan.

ParameterTypeDescription
creditsnumberRequired. Extra credits to buy: 5,000 to 50,000, in steps of 5,000. They never expire and are used after the monthly allowance.

Returns payment_url, credits, price_usd, currency, network_id, expires_in_seconds and instructions. POST to payment_url, answer its 402 payment challenge with a Shared Payment Token issued to network_id, and retry: the retry adds the credits and returns a receipt. Credits are priced at your package's own per-1,000 rate.

low_balance

search, fetch, search_jobs and the company technology tools report credits_remaining: in usage for search, as a string in metadata for fetch, and in metadata for the others. When fewer than 20% of your allowance are left - never fewer than 100 credits, and 200 of the free credits on Free - the result also carries a low_balance block:

{
  "low_balance": {
    "remaining": 150,
    "message": "This JobsPipe account has 150 credits left of 1,000. Tell the user; if they want more, call upgrade_plan and show them the Stripe link it returns.",
    "upgrade": "call upgrade_plan"
  }
}

It is only a notice: the search itself worked. The fields are additive, so a client that ignores them sees the results it always did.

Limits and errors

The MCP server inherits your plan's limits from the REST API. Only authentication failures come back as an HTTP status. Everything that happens inside a tool — including an exhausted quota or a rate limit — comes back as a normal tool result with isError: true and a JSON body such as { "error": "Monthly request quota exceeded" }, so the agent can read it and react.

WhereWhat you getMeaning
HTTP 401Connect with OAuth, or send a JobsPipe API key (jp_live_) ..., with signup_url and docs_urlNo Authorization: Bearer or x-api-key credential was sent.
HTTP 401Access token is invalid or expired. Reconnect to continue.The bearer is not a jp_live_ key and not a live OAuth token.
Tool result, isError{ "error": "Invalid API key" }The jp_live_ key was not accepted by the API.
Tool result, isError{ "error": "Monthly request quota exceeded", "message": "...", "upgrade_url": "...", "next_step": "..." }Your plan's monthly job quota is used up. next_step tells the assistant to call upgrade_plan for a direct Stripe checkout link; upgrade_url opens billing with a package sized to your use; saved signals keep working.
Tool result, isError{ "error": "Rate limit exceeded", "retryable": true, "retry_after": 1 }Per-second rate limit exceeded. For a search or other read, the server already waited and retried once. A tool that changes something (create_signal, update_signal, delete_signal, give_feedback, upgrade_plan) is never sent twice by the server: wait retry_after seconds and call it again yourself.

Every error from the API carries retryable: true when the same call may work later (a rate limit or a temporary server error), false when it will not until something changes.

Call get_account_info to see your own plan, limits and remaining credits, and list_pricing_plans to see the quota, rate limit, and max results per call for each plan.

Other agent surfaces

Beyond MCP, JobsPipe is discoverable by autonomous agents:

On this page