Company Search API

    Company Search API

    Company Search finds companies, not pages. Describe the companies you want in plain language, for example "Series A fintech startups in Berlin building payment infrastructure". Desearch looks for their websites, reads each homepage, and scores how well the company fits. It then reads the company's contact, about, team, and imprint pages for emails, phones, and social profiles, finds its leaders (CEO, founders, CTO, and CFO by default) with their LinkedIn profiles, infers their emails from the company's address pattern, and checks every email.

    Company Enrich collects the same data for companies you already know. You send domains, and you get one company per domain, without a fit score.

    Every contact keeps the page it came from. Public data only: what the company publishes on its own site, what web search returns, and public LinkedIn profiles. An inferred email is a guess from the company's address pattern and says so (source: "inferred").

    Who this page is for

    Humans

    Start a task with one POST, then poll it, read the companies it delivered, and download them as CSV. The task runs in the background, and companies arrive while it runs. Full schemas are in the API Reference.

    Agents / tool authors

    A task runs in the background. The POST returns a task_id at once, with no companies in it. Poll GET /desearch/companies/tasks/{task_id} and read GET /desearch/companies/tasks/{task_id}/results with the cursor it returns. A company is published before every stage has run, and later stages fill it in: read again without cursor to get the latest version of every company. The POST is the only billed call. Send an Idempotency-Key header so a retried call does not start, or pay for, a second task.

    Company Search or Company Enrich?

    Company SearchCompany Enrich
    RoutePOST /desearch/companies/searchPOST /desearch/companies/enrich
    You sendA description (query)1 to 200 domains or URLs (domains)
    You getUp to count companies that match, each with a fit scoreOne company per domain you send, with score: null
    Task search_type and platformcompany_search, companycompany_enrich, company_enrich
    Rate$90 per 1,000 companies ($0.09 each)$50 per 1,000 companies ($0.05 each)
    Minimum billed per task that delivers any company2 companies1 company

    Use Company Search to build a list of accounts that you do not have yet. Use Company Enrich when you already have the list, from a CRM, a spreadsheet, or a Company Search, and need contacts and leaders for it. Both return the same company fields, run on the same task routes, and bill only the companies they deliver.

    Which surface should I use?

    GoalUseNot this
    A list of companies that match a description, with their contacts and leadersCompany Search (this page)Web Search, which returns pages
    Contacts and leaders for domains you already haveCompany Enrich (this page)Extract on each page yourself
    People or creators by what they post or how they describe themselvesPeople SearchCompany Search
    The text of one page you already haveExtractCompany Enrich

    Base URL: https://api.desearch.ai
    Auth: Authorization: $DESEARCH_API_KEY (raw key in examples).

    First call: Company Search

    bash
    curl --request POST 'https://api.desearch.ai/desearch/companies/search' \ --header "Authorization: $DESEARCH_API_KEY" \ --header 'Content-Type: application/json' \ --header "Idempotency-Key: $(uuidgen)" \ --data '{ "query": "Series A fintech startups in Berlin building payment infrastructure", "count": 25, "filters": {"countries": ["DE"], "require": ["email"]} }'

    The response is 202 Accepted with the new task. Its status is queued, and reserved_cost_usd is the amount reserved for count companies. Keep task_id for every call below. Repeating the call with the same Idempotency-Key returns 200 with the same task and is not charged again.

    Request body fields:

    FieldMeaning
    queryRequired. The companies you are looking for, in plain language: what they do, where, how far along. 3 to 1000 characters.
    countHow many companies to deliver. 5 to 200, default 25. The task reserves this many and bills only the companies it delivers.
    min_scoreOnly deliver companies with at least this fit score, 0 to 100.
    excludeCompanies to leave out, as domains or URLs, for example ones you already contacted. Each is reduced to its host without www.. Up to 1,000.
    rolesThe leaders to look for: any of ceo, founder, cto, cfo, coo, cmo, president, owner, managing_director. Default ["ceo", "founder", "cto", "cfo"]. A managing director (Geschäftsführer) counts as ceo when ceo is asked for.
    infer_emailsDefault true. Guess leaders' emails from the company's address pattern (for example first.last), learned from the addresses found on its site. Inferred emails say source: "inferred" and carry a confidence.
    verify_emailsDefault true. Check every email: syntax, mail server (MX), and, for inferred ones, a web search. Each email then has a verification. Turn it off to finish sooner without it.
    filters.countriesWhere the companies should be: ISO 3166-1 alpha-2 codes (DE) or country names. Scoring treats them as a location criterion. Up to 20.
    filters.requireOnly deliver companies that have all of these: email, phone, linkedin (the company's LinkedIn page), leader (a leader with one of roles), leader_email (a leader with an email). A company that does not have them is not delivered and not billed.
    metadataYour own labels for the task, for example {"campaign": "q4"}. Returned on the task and usable as a filter on the task list. The task never uses them. See Label tasks with metadata below.

    The body accepts only these fields. An unknown field fails validation with 422.

    Request header, the same on both create routes:

    HeaderMeaning
    Idempotency-KeyOptional. Any unique string, for example a UUID. Surrounding whitespace is removed and the value is cut to 200 characters. Sending the same key again returns the first task (HTTP 200) and refunds this call's reservation, so it is not charged. A new key starts a new task (HTTP 202).

    First call: Company Enrich

    bash
    curl --request POST 'https://api.desearch.ai/desearch/companies/enrich' \ --header "Authorization: $DESEARCH_API_KEY" \ --header 'Content-Type: application/json' \ --header "Idempotency-Key: $(uuidgen)" \ --data '{"domains": ["acme.com", "https://www.beta.io/about"], "roles": ["ceo", "cto"]}'
    FieldMeaning
    domainsRequired. 1 to 200 company domains or URLs, such as acme.com or https://www.acme.com/about. Each is reduced to its host without www. and lowercased, and duplicates are removed. An IP address, localhost, or anything that is not a public host name fails with 422.
    roles, infer_emails, verify_emails, metadataThe same as on Company Search.

    On the task, count is the number of domains left after deduplication, and query is a short label of them, such as acme.com, beta.io or acme.com, beta.io and 3 more. request.domains lists the normalized domains.

    In Python, call company_enrich(domains=[...]). In JavaScript, call companyEnrich({ domains: [...] }). Both return the same task as Company Search.

    How a task runs

    Each company goes through five stages. A company is published as soon as the stages its request needs have run, and the later stages fill it in while you read it.

    StageWhat it does
    profileReads the homepage: name, description, structured data, social profiles, and the emails and phones on it. Company Search also scores the company here.
    contactsReads up to 4 more pages of the company's own site (contact, imprint or impressum, team, about) for emails, phones, and social profiles.
    peopleFinds the leaders with one of roles: first on the company's own pages, where the quote that names them must be on the page, then on LinkedIn for the roles still missing.
    email_patternsLearns the company's address pattern from the names and emails it found, and infers the leaders' emails. Skipped when infer_emails is false.
    email_checkChecks every email: syntax, mail server, and, for an inferred email, a web search for the address. Skipped when verify_emails is false.

    When a company is published:

    filters.require containsPublished after
    Nothing (and every Company Enrich task)profile
    email, phone, or linkedincontacts
    leaderpeople
    leader_emailemail_check

    A company that does not meet filters.require once its stage has run is not delivered and not billed. The task counts it in progress.filtered, for example no_email.

    enrichment.status on each company is partial while any stage is pending, then complete. enrichment.stages shows each stage as pending, done, failed, or skipped. On the task, stage moves through planning, searching, processing, and finalizing, and is enriching once every company is published and only the later stages of some of them are left. The task ends only after those stages have run, so read the companies after it ends to get their final version. When the task's time limit stops the later stages, the stages left are skipped, enrichment.errors says the time limit was reached, and the task has the enrichment_time_limit warning.

    Poll the task

    bash
    curl --request GET "https://api.desearch.ai/desearch/companies/tasks/$TASK_ID" \ --header "Authorization: $DESEARCH_API_KEY"

    Poll every few seconds. The task moves through queued, running, and sometimes pausing, paused, or cancelling, then ends as completed, partial, failed, cancelled, or expired.

    Task fieldMeaning
    task_id, search_type, platformThe task and the product it runs: company_search and company, or company_enrich and company_enrich.
    status, stage, progressWhere the task is.
    queryCompany Search: the description. Company Enrich: a short label of the domains.
    count, result_countCompanies asked for (Company Enrich: the domains left after deduplication) and companies delivered so far.
    requestThe normalized request. Company Search: query, count, min_score, exclude, roles, infer_emails, verify_emails, filters. Company Enrich: domains, roles, infer_emails, verify_emails.
    metadataThe labels sent with the task, or {} when it had none.
    interpretedCompany Search: how the description was understood, with its criteria and the queries it ran.
    completion_reasonWhy the task ended, below. null while it is queued, running, or paused.
    coverage_limitedtrue when the task stopped on a budget (completion_reason cost_limit, candidate_budget, or time_limit) before reaching count: more companies may match than it returned. Default false. status is still completed.
    warningsWhat made part of the task less reliable without failing it, below. [] when nothing did.
    reserved_cost_usdAmount reserved when the task was created.
    final_cost_usd, refunded_usdFinal cost and the returned part of the reservation, once the task has ended.
    created_at, started_at, paused_at, finished_atWhen the task was created, started, last paused, and ended.
    results_expire_atWhen the results stop being available, 30 days after the task ends.
    linksPaths for this task, its results, and its export.

    completion_reason is one of:

    ValueMeaning
    count_reachedIt delivered count companies.
    all_domains_processedCompany Enrich: it read every domain.
    rounds_exhaustedIt ran every search it planned and delivered fewer than count.
    no_candidatesIts searches found no company to check.
    zero_qualifiedIt checked the companies its searches found and none fit.
    candidate_budgetIt checked as many candidates as a task of this count may.
    cost_limitWhat searching cost reached what its results would bill, so it stopped.
    time_limitIt ran for its maximum time.
    cancelledIt was cancelled.
    failedAn error ended it.

    progress holds counters while the task runs:

    KeyMeaning
    candidates_found, candidates_processed, qualifiedCompanies the searches found, companies checked, and companies that fit.
    filteredCompanies left out, by reason, for example unreachable (the site could not be read), not_a_company, below_min_score, and no_<requirement> for a filters.require entry, such as no_email.
    enrichmentpublished, complete, pending, and failed: companies delivered, and how many of them have every stage done.
    email_checkschecked, valid, invalid, risky, and unknown: email checks run, by result.
    stop_reasonThe worker's internal reason for stopping. Prefer completion_reason and coverage_limited.

    Each item in warnings has a code and a message. Codes the API uses today:

    codeMeaning
    planning_fallbackThe search was planned without AI, so results may be broader.
    ai_scoring_unavailableAI scoring was unavailable, so companies were matched by the search's words only.
    enrichment_time_limitSome companies were not fully enriched before the task's time limit. Their remaining stages are skipped, with an enrichment.errors entry.
    linkedin_verification_unavailableLeaders found through LinkedIn search could not be checked against their LinkedIn profiles.

    Treat an unknown code as informational and show its message.

    Read the results

    Companies arrive while the task is still running. Read them in the order they were found. Leave out cursor on the first call, then pass each response's next_cursor back as cursor:

    bash
    curl --request GET "https://api.desearch.ai/desearch/companies/tasks/$TASK_ID/results?limit=100" \ --header "Authorization: $DESEARCH_API_KEY"

    The response has task_id, status, results (the companies), next_cursor, and has_more. limit is 1 to 500, default 100.

    • When has_more is false, you have every company delivered so far. Keep the last next_cursor and call again later to get only the companies delivered since.
    • A cursor returns only companies delivered after it. Companies you already have are filled in place by the later stages. To get their latest version, read again without cursor, or wait until the task has ended and read once.
    • Check the task, not the results page, for the end: the task has ended when its status is completed, partial, failed, or cancelled.

    Reading results

    A finished task says why it stopped, and each company says how complete it is and what its own pages confirm. Check these before you use the list:

    FieldWhat it tells youWhat to do
    completion_reasoncount_reached or all_domains_processed: it delivered what you asked for. rounds_exhausted: it ran every search it planned and delivered fewer. no_candidates or zero_qualified: it found no company, or none that fit, and costs nothing.When it delivered fewer companies than you asked for, describe them more broadly.
    coverage_limitedtrue when a budget stopped the task before count, so more companies may match.To look further, start a new search and put the domains you already have in exclude.
    enrichment.status, enrichment.stagespartial while later stages are still to run, complete when none is pending. A stage is failed when it could not run, and skipped when the request turned it off or the time limit stopped it.On a running task, read again without cursor for the latest version. Check enrichment.errors for stages that failed.
    primary_email, primary_contactThe company's best email and the person to write to first.Use these for outreach. Check verification.status before you send.
    emails[].verification.statusvalid, invalid, risky, or unknown.Do not send to invalid. Treat risky and unknown with care.
    people[].verifiedtrue when a quote on the company's own site or the person's current LinkedIn position shows the role.Check evidence for leaders that are not verified.
    score.criteria_checks, score.fully_verifiedWhat the company's own pages confirm, criterion by criterion. Company Search only.Read the checks that are not met to see what is unconfirmed. The company still qualified.

    Example company

    A complete Company Search result:

    json
    { "id": "company:acme.com", "platform": "company", "handle": "acme.com", "domain": "acme.com", "url": "https://acme.com/", "name": "Acme", "description": "Payment infrastructure for marketplaces.", "logo_url": "https://acme.com/logo.png", "location": {"country": "DE", "city": "Berlin", "address": "Torstraße 1, 10119 Berlin"}, "industry": "Fintech", "emails": [ { "value": "hello@acme.com", "type": "general", "source": "website", "source_url": "https://acme.com/impressum", "on_domain": true, "person": null, "confidence": null, "pattern": null, "verification": { "status": "valid", "checks": {"syntax": true, "mx": "ok", "smtp": "skipped", "web": "skipped"}, "checked_at": "2026-10-28T10:00:00Z" } } ], "phones": [ {"value": "+493012345678", "type": "main", "source": "website", "source_url": "https://acme.com/impressum", "raw": "+49 30 1234 5678"} ], "socials": [ {"type": "linkedin", "url": "https://www.linkedin.com/company/acme", "handle": "acme", "source": "website", "source_url": "https://acme.com/"} ], "pages": [ {"type": "home", "url": "https://acme.com/"}, {"type": "imprint", "url": "https://acme.com/impressum"} ], "people": [ { "name": "Anna Schmidt", "title": "Co-founder & CEO", "roles": ["ceo", "founder"], "linkedin_url": "https://www.linkedin.com/in/annaschmidt", "x_url": null, "github_url": null, "emails": [ { "value": "anna.schmidt@acme.com", "type": "personal", "source": "inferred", "source_url": null, "on_domain": true, "person": "Anna Schmidt", "confidence": "high", "pattern": "first.last", "verification": { "status": "valid", "checks": {"syntax": true, "mx": "ok", "smtp": "skipped", "web": "found"}, "checked_at": "2026-10-28T10:00:05Z" } } ], "phones": [], "evidence": {"quote": "Anna Schmidt, Geschäftsführerin", "source_url": "https://acme.com/impressum", "source": "website"}, "verified": true } ], "primary_email": { "value": "anna.schmidt@acme.com", "type": "personal", "source": "inferred", "source_url": null, "on_domain": true, "person": "Anna Schmidt", "confidence": "high", "pattern": "first.last", "verification": { "status": "valid", "checks": {"syntax": true, "mx": "ok", "smtp": "skipped", "web": "found"}, "checked_at": "2026-10-28T10:00:05Z" } }, "primary_contact": { "name": "Anna Schmidt", "title": "Co-founder & CEO", "email": "anna.schmidt@acme.com", "linkedin_url": "https://www.linkedin.com/in/annaschmidt", "reason": "CEO, email inferred from the company's pattern and verified" }, "contact_coverage": {"email": true, "phone": true, "linkedin": true, "leader": true, "leader_email": true}, "enrichment": { "status": "complete", "stages": {"profile": "done", "contacts": "done", "people": "done", "email_patterns": "done", "email_check": "done"}, "errors": [] }, "score": { "value": 86, "qualified": true, "relevance": "high", "summary": "Berlin payments infrastructure startup that raised a Series A in 2025.", "matched_signals": ["Payment infrastructure for marketplaces", "Based in Berlin"], "reasons": ["Builds payment infrastructure", "Headquartered in Berlin"], "concerns": ["The funding stage is not stated on the company's own pages"], "criteria_checks": [ { "criterion": "Builds payment infrastructure", "verdict": "met", "quote": "Payment infrastructure for marketplaces.", "source_url": "https://acme.com/", "reason": "The homepage describes the product.", "is_disqualifier": false }, { "criterion": "Raised a Series A", "verdict": "unknown", "quote": null, "source_url": null, "reason": "No page the task read mentions the funding round.", "is_disqualifier": false } ], "fully_verified": false }, "found_by": [ {"query": "fintech payment infrastructure startup Berlin", "url": "https://acme.com/", "title": "Acme | Payments for marketplaces", "kind": "direct"} ] }

    A Company Enrich result has the same fields with "score": null, and found_by lists the domain you sent (kind: "domain"). Fields that a later stage has not filled in yet are empty lists or null. Treat a missing value as unknown, not as absent.

    Company fields

    FieldMeaning
    id, platform, handle, domaincompany:<domain>, always company, and the company's domain (twice).
    url, name, description, logo_url, industryFrom the homepage.
    locationcountry, city, and address, when the site states them.
    emailsThe company's emails, below.
    phonesUp to 4 phone numbers, below.
    socialsThe company's social profiles, below.
    pagesThe pages that were read, each with type (home, contact, about, team, imprint, legal, other) and url.
    peopleLeaders with one of the request's roles, below.
    primary_emailThe company's best email, an item like those in emails, or null.
    primary_contactThe person to contact first, or null.
    contact_coverageWhat was found: email, phone, linkedin, leader, and leader_email, the same names as filters.require, each true or false.
    enrichmentstatus, stages, and errors, below.
    scoreThe fit score, below. null on Company Enrich.
    found_byHow the company was found, below.

    Emails

    Each item in emails, in people[].emails, and primary_email has:

    FieldMeaning
    valueThe address.
    typepersonal, leadership, sales, general, press, support, careers, legal, or other. Two names joined by ., -, or _ read as personal. A single word that is no known role is other until it matches a leader's name, so anna@ is personal for Anna Schmidt.
    sourcewebsite, search, linkedin, or inferred.
    source_urlThe page the email was found on. null when it was inferred.
    on_domaintrue when the address is on the company's domain.
    personThe person the address belongs to, when known.
    confidenceInferred emails only: high when the pattern was learned from this company's own addresses, low for a common pattern that was not learned.
    patternInferred emails only: the pattern used, such as first.last.
    verificationThe email check, or null until email_check has run (and always when verify_emails is false).

    verification has status, checks, and checked_at:

    FieldValues
    statusvalid, invalid, risky (a catch-all domain, or no MX record but an A record), or unknown (nothing could confirm or refute it).
    checks.syntaxtrue when the address is well formed.
    checks.mxok, implicit (no MX record, but the domain has an A record), none, or error.
    checks.smtpaccepted, rejected, catch_all, unreachable, or skipped when no SMTP check ran.
    checks.webfound when a web search finds the address, not_found, or skipped (addresses that were not inferred).

    Which emails are kept:

    • An address is kept when it is on the company's domain, at a free mail provider, or on a domain with the same name (acme.io listing acme.com). Addresses of other organizations listed on imprint pages, such as regulators, ombudsmen, or processors, are dropped.
    • System addresses (noreply@, postmaster@, bounce@, mailer-daemon@) and junk (image names such as logo@2x.png, example and placeholder addresses, vendor addresses such as @sentry.io or @wixpress.com) are dropped.
    • After email_check, a person keeps at most one low-confidence inferred email: the first valid one, else the first.last guess.

    primary_email is only ever a personal, leadership, sales, or general address. support@, press@, jobs@, privacy@, and other addresses stay in emails but never become the company's primary email. An inferred email becomes the primary email only when its check is valid.

    Phones and socials

    phones[] fieldMeaning
    valueE.164 when the number parses, otherwise the digits as written.
    typemain, sales, support, mobile, or other. Fax numbers are dropped.
    rawThe number as written on the page.
    source, source_urlWhere it was found.

    A company has at most 4 phones: tel: links and structured data first, then numbers labelled in the page text.

    socials[] fieldMeaning
    typelinkedin, x, facebook, instagram, github, youtube, tiktok, crunchbase, discord, telegram, or medium.
    url, handleThe profile URL and its handle. Share and intent links are dropped.
    source, source_urlWhere it was found.

    People

    Each item in people is a leader:

    FieldMeaning
    name, titleThe person and their title as written, such as Co-founder & CEO.
    rolesWhich of the request's roles the person holds, such as ["ceo", "founder"]. A managing director counts as ceo when ceo was asked for.
    linkedin_url, x_url, github_urlTheir profiles, when found.
    emails, phonesTheir own contacts, in the same shape as the company's.
    evidenceWhat shows the role: quote, source_url, and source (website, linkedin, or search).
    verifiedtrue when a quote on the company's own site or the person's current LinkedIn position shows the role.

    primary_contact has name, title, email, linkedin_url, and reason, a short line on why this person was picked, such as CEO, email inferred from the company's pattern and verified.

    Enrichment

    FieldMeaning
    enrichment.statuspartial while any stage is pending, then complete.
    enrichment.stagesprofile, contacts, people, email_patterns, and email_check, each pending, done, failed, or skipped. skipped means the request turned the stage off, such as infer_emails: false, the stage had nothing to do, or the task's time limit stopped it.
    enrichment.errorsStages that failed or were stopped, each with stage and message. The company keeps what the other stages found.

    Score

    Company Search scores each company against the description. Company Enrich does not, and its score is null.

    FieldMeaning
    valueThe fit score, 0 to 100. min_score filters on it.
    qualified, relevanceWhether the company fits, and high, medium, or low.
    summaryOne line on why this company matched.
    matched_signals, reasons, concernsWhat matched, why it fits, and what is uncertain.
    criteria_checksOne check per criterion: what the company's own pages confirm. Information only: a qualified company can still have criteria that are unknown.
    fully_verifiedtrue when every criterion is met and no disqualifier (is_disqualifier) is.

    Each check in criteria_checks has:

    FieldMeaning
    criterionOne of the search's criteria, word for word.
    verdictmet: a quote from the company's pages shows it (quotes are checked word for word; one that is not there becomes unknown). not_met: the evidence contradicts it. unknown: nothing found shows it either way. For a disqualifier, met means it applies.
    quoteThe verbatim text behind a met verdict, or null.
    source_urlThe page the quote is on, or null.
    reasonA short explanation of the verdict.
    is_disqualifiertrue when the criterion is a disqualifier, so met means it applies.

    Found by

    Each item in found_by has query (the search that found the company), url, title, and kind: direct (a hit on the company's own site), list (the company was listed on a page the search read, such as a directory), or domain (Company Enrich: a domain you sent).

    Download CSV or JSON Lines

    bash
    curl --request GET "https://api.desearch.ai/desearch/companies/tasks/$TASK_ID/export?format=csv" \ --header "Authorization: $DESEARCH_API_KEY" \ --output companies.csv

    format is csv (default) or jsonl. The export has every company delivered so far, in the order they were found. Fields that later stages have not filled in yet are empty, so export a running task again after it ends. The file is named company-search-<task_id>.csv or company-enrich-<task_id>.csv (.jsonl for JSON Lines).

    format=jsonl returns one company per line, with every field above.

    The CSV has one row per company and these columns:

    ColumnsMeaning
    domain, name, url, scoreThe company and its fit score (empty on Company Enrich).
    country, city, industryFrom location and industry.
    primary_email, primary_email_typeThe company's best email and its type.
    emails, phonesEvery company email and phone, separated by ; .
    linkedin, x, facebook, instagram, github, youtube, tiktokThe company's profile URL on each network.
    ceo_name, ceo_title, ceo_email, ceo_email_status, ceo_linkedinThe CEO, or a managing director when there is no CEO.
    founder_*, cto_*, cfo_*The same five columns (_name, _title, _email, _email_status, _linkedin) for the founder, the CTO, and the CFO.
    fully_verifiedscore.fully_verified, or empty when it is unknown.
    unverified_criteriaThe criteria that are not met, plus the disqualifiers that are met, separated by ; .
    enrichment_statuspartial or complete.

    A leader's email column shows their first valid email, else the first one not known to be invalid, else the first. Leaders who hold none of these roles, such as a COO, are only in the JSON Lines export. A managing director fills the CEO columns when the company has no CEO.

    Results stay available for 30 days after the task ends. After that, results and export return 410.

    Pause, resume, or cancel

    CallWhat happens
    POST /desearch/companies/tasks/{task_id}/pauseA queued task pauses at once. A running task finishes its current batch first (pausing, then paused). Companies delivered so far stay available. A task paused for more than 7 days is cancelled.
    POST /desearch/companies/tasks/{task_id}/resumeContinues a paused task from where it stopped. It is not charged again.
    POST /desearch/companies/tasks/{task_id}/cancelStops the task. You pay only for the companies it delivered, and a task cancelled before it starts costs nothing. Companies delivered so far stay available for 30 days.

    When the task's status does not allow the action, the call returns 409 with detail and the current task.

    List tasks

    GET /desearch/companies/tasks lists your company's Company Search and Company Enrich tasks, newest first.

    ParameterMeaning
    typesearch (Company Search) or enrich (Company Enrich). Leave it out for both.
    statusComma-separated statuses, for example running,paused.
    metadata.<key>metadata.<key>=<value>: only tasks with that label, below. Up to 3.
    limit, cursorPage size (1 to 100, default 20) and the next_cursor from the previous page.

    People Search tasks are not listed here, and a People Search task_id returns 404 on the company task routes (and a company task_id returns 404 on the People Search routes).

    Label tasks with metadata

    Add metadata to a Company Search or Company Enrich request to tag the task with your own labels, such as the campaign, workspace, or customer it runs for. The task never reads them, so labels do not change what is found or what it costs.

    bash
    curl --get 'https://api.desearch.ai/desearch/companies/tasks' \ --header "Authorization: $DESEARCH_API_KEY" \ --data-urlencode 'type=search' \ --data-urlencode 'metadata.campaign=q4' \ --data-urlencode 'metadata.workspace_id=ws_42'

    A task must match every filter exactly. When you page with cursor, send the same filters on every page.

    RuleLimit
    Labels per taskUp to 10 keys.
    Keys1 to 40 characters: lowercase a-z, 0-9, _, ., or -.
    ValuesStrings of up to 200 characters.
    Filters per task list callUp to 3 metadata.<key>=<value> parameters.

    A request whose metadata breaks these rules fails with 422. On the task list, an invalid metadata.<key> filter returns 400.

    Billing

    • Starting a task is the only billed call. Company Search reserves count companies at $0.09 each ($90 per 1,000). Company Enrich reserves one company per domain you send at $0.05 each ($50 per 1,000). The response carries the usual X-Desearch-Cost-Usd, X-Desearch-Usage-Count, X-Desearch-Service (Company Search or Company Enrich), and X-Desearch-Currency headers, and the task shows reserved_cost_usd.
    • When the task ends, you pay only for the companies it delivered, with a minimum of 2 for Company Search and 1 for Company Enrich when it delivers any. You never pay more than the reservation. The rest of the reservation is returned automatically, including duplicate domains, and the task shows final_cost_usd and refunded_usd.
    • A company left out by min_score or filters.require, or whose site cannot be read, is not delivered and not billed. A task that delivers no companies is free, and so is a task cancelled before it starts.
    • Polling, results, exports, pause, resume, cancel, and the task list are free.
    • The same Idempotency-Key returns the first task and is not charged again.
    • 402 means the balance cannot cover the reservation. 429 means too many of your People Search and Company Search tasks are already queued, running, or paused.

    Rates are on the pricing page, as of 28 October 2026. Console shows the rates for your account.

    Errors

    StatusMeaning
    400Unknown type, status, or cursor, or an invalid metadata.<key> filter on the task list.
    401Missing API key, or a key that is not valid.
    402Not enough credits for the reservation.
    403A malformed Authorization value, such as an unreplaced placeholder like YOUR_API_KEY. See Auth and API keys.
    404Task not found, including a People Search task_id on these routes.
    409Pause, resume, or cancel does not apply to the task's current status. The body has detail and the current task.
    410Results expired, 30 days after the task ended.
    422Invalid request body or parameters: for example a query under 3 characters, count outside 5 to 200, an unknown role or filters.require value, more than 200 domains, a domain that is an IP address or localhost, an unknown field, or metadata that breaks the label rules.
    429Too many People Search and Company Search tasks already queued, running, or paused.

    SDKs and MCP

    • Python: desearch-py 1.10.0 adds company_search, company_enrich, list_company_tasks, get_company_task, get_company_results, pause_company_task, resume_company_task, cancel_company_task, export_company_results, and the helpers wait_for_company_task and iter_company_results, with the models Company, CompanyTask, and the others. A 409 from pause, resume, or cancel raises CompanyTaskConflictError (.detail, .task), and a timeout in wait_for_company_task raises CompanyTaskTimeoutError while the task keeps running. 1.10.0 is not on PyPI yet; install it from GitHub with pip install "desearch-py @ git+https://github.com/Desearch-ai/desearch.py@864469c5f8e477ef0ef2142063a558c3849850aa". See the Python SDK.
    • JavaScript: desearch-js 1.11.0 adds companySearch, companyEnrich, listCompanyTasks, getCompanyTask, getCompanyResults, pauseCompanyTask, resumeCompanyTask, cancelCompanyTask, exportCompanyResults, and the helpers waitForCompanyTask and iterCompanyResults, with types such as Company and CompanyTask. Pause, resume, and cancel throw CompanyTaskConflictError on HTTP 409. See the JavaScript SDK.
    • MCP: from desearch-mcp-server 0.6.0, the tools company-search, company-enrich, company-search-status, company-search-tasks, company-search-pause, company-search-resume, and company-search-cancel. See MCP.

    Related