GET /account/credits: per-top-up breakdown with credit_topups
Account credits responses now include a credit_topups
array for accounts with live top-ups — one entry per top-up still holding
credits, sorted soonest expiry first:type: how the top-up entered your account —purchase(one-time purchase),auto_topup(automatic reload), orgranted(added by Crustdata).granted_credits/remaining_credits: what the top-up originally added and what’s still unspent.granted_at/expires_at: when it was added and when its unspent credits expire (expires_atisnullfor top-ups without an expiry).
wallets, the list is informational — keep gating spending
decisions on account.credits. Available on GET /account/credits with
x-api-version: 2025-11-01.Company Search: filter by any office location with locations.all_office_addresses
Company Search now accepts
locations.all_office_addresses as a filter field, so you can match
companies on any of their offices, not only the headquarters that the other
locations.* filters target. The field was already returned in responses; it
is now filterable too.- Substring match: office addresses are free-text strings (e.g.
"HITEC City, Madhapur, Hyderabad, Telangana, 500081, IN"), so filter with a city or region substring using(.)/contains. - Combines with HQ filters: pair it with
locations.countryin anandgroup to express queries like headquartered in the USA with an office in Hyderabad:locations.country="USA"andlocations.all_office_addresses(.)"Hyderabad". - Not sortable: the field is filterable and returned, but not valid in
sorts. See Locations in the search reference.
POST /company/search with x-api-version: 2025-11-01.is_null and is_not_null filter operators on Person Search
Person Search now accepts the is_null and is_not_null filter
operators, so you can match on field presence directly instead of fetching
fields and checking for null in your own code.is_null— matches profiles where the field has no value. On nested-array fields the check is element-based: an emptyexperience.employment_details.currentarray matchesexperience.employment_details.current.nameis_null.is_not_null— matches profiles where at least one non-null value is present for the field.valueis required — send a literal"value": null; omitting thevaluekey returns400.
experience.employment_details.current.name is_null and
experience.employment_details.past.name is_not_null in one and group.
See null checks
for details.Available on POST /person/search with x-api-version: 2025-11-01.
Company Search and Job Search already accept both operators.Every API response now reports what it cost: the X-Credits-Used header
Every response from the data APIs — search, enrich, identify, autocomplete,
web, and batch — now carries an X-Credits-Used header with the exact decimal
credits that request deducted, read from the same ledger that bills you. Log it
per call to attribute spend by customer, agent, or workflow, sum it to keep an
automation inside a budget, or reconcile it against invoices — without polling
the balance endpoint.- Exact decimal —
0.03for a one-result person search,3for a three-company enrich. Fractional costs report exactly. - Present on error responses too — errors report
0, so the header is there on every call you log. 0is meaningful — endpoints that consume no credits report0, and so do asynchronous submissions (batch jobs and background-job searches), which are billed when the job runs rather than at submit time.- Readable from browsers — the header is exposed cross-origin, so dashboard-style apps can read it from JavaScript.
- Declared in the specs — every response in the OpenAPI specs documents the header.
429s, carry no header. See
Per-call usage on the Credits page
for details and examples.Available on every data API endpoint.Job Search: company job-board postings, job_details.source, and job_details.employment_type
Job Search now includes postings published
directly on company job boards — about
3 million postings joining the roughly 45 million existing listings. Two new
job_details fields expose this:job_details.source— the platform each posting comes from. Present on every job:professional_networkfor the existing listings, or one of the job-board platforms —workday,smartrecruiters,greenhouse,workable,lever,icims,ashby,rippling,yc. Filterable (=,in, …) and groupable viagroup_by; not sortable. See the Job details catalog.job_details.employment_type— the employment type declared by the posting:full_time,part_time,contract,intern, ortemporary. Currently populated for roughly 85% of job-board postings andnullfor now whensourceisprofessional_network. Filterable and groupable; not sortable.- Search-only — both fields are returned by
POST /job/searchonly; Live Search does not include them.
POST /job/search with x-api-version: 2025-11-01.Company Search: geo_distance and geo_exclude on company headquarters
Company Search now filters companies by
true geographic radius around their headquarters. Pick a point — either a place
name like “Palo Alto, CA” or a pair of coordinates — and set a distance.
geo_distance then returns every company whose headquarters falls within that
distance of the point, instead of matching on location text and listing city
names one at a time.geo_distance— returns companies headquartered inside the radius.geo_exclude— the inverse ofgeo_distance. It returns companies headquartered outside the radius and excludes the ones inside it, so you can skip a region you already cover. See the geographic radius filters reference.- Setting the point and the distance — the point is what the radius is
measured from. Pass it as
location(a place name, geocoded server-side) or aslat_lng([lat, lng], which skips geocoding), then add adistanceand aunitsuch askmormi. - One field path — apply both operators to
locations.headquarters, which targets the company’s geocoded headquarters point. - Normalized location facets —
locations.stateandlocations.cityare newly filterable and match normalized values derived from geocoding the headquarters address, with search responses returning the same normalized values.locations.countryaccepts both ISO-3 codes ("USA") and full country names ("United States") — either form matches the same companies, so existing filters keep working — and responses return the normalized full country name. See Locations.
POST /company/search with x-api-version: 2025-11-01.Auto top-up: refill credits automatically before the balance runs out
Save a card once, set a threshold and a reload amount, and Crustdata tops your balance up on its own. The balance is checked every time credits are spent, and the moment it drops below your threshold the saved card is charged for the reload package and the credits land immediately — instead of buying credits by hand each time the balance runs down. The feature is free on every plan and stays off until you turn it on.- Turn it on from the Credits page — save a card through a Stripe-hosted page and accept the auto-charge consent, then set two numbers: a threshold and a reload amount. Enabling requires a balance above the threshold you pick, so buy credits manually once if you are already below it. Turning it off is one click, any time.
- What a top-up does — the saved card is charged at the price shown during setup, the live balance refreshes right away, and you get a receipt email plus a real Stripe invoice in the Invoices tab, identical to a manual purchase.
- Monthly recharge limit — an optional cap on how many credits auto top-up can add per calendar month. It resets each calendar month.
- Safety rails — at most 3 auto top-ups per day per account, and double-charge protection means one threshold breach can only ever produce one charge.
- Failed charges — a declined or expired card gets an email with the reason and the fix, Crustdata retries after a cooldown, and after 3 consecutive failures auto top-up disables itself and says so. Cards nearing expiry get a warning email before they lapse.
- Thresholds and reload size — the threshold is an absolute credit balance, and the minimum reload is 500 credits. The exact price is shown before you save.
- Pricing — the feature is free, with no credit cost and no plan gate. Each top-up buys the selected credit package at the standard self-serve rate — the same volume-tiered pricing as a manual purchase.
- Coming next — negotiated per-account rates applied to auto top-ups. Every top-up charges the standard self-serve rate today.
Person Search: match conditions across different entries in a profile
Person Search addsall_of, a composite
operator whose conditions are each matched by a different entry in a
person’s history array. A person record holds arrays with many entries — one
for every past job and every degree — so “Engineer at Company A and Manager at
Company B” now returns people who held each title at each company, rather than
one job carrying both titles. A plain and on the same array path keeps its
existing same-entry meaning, so your current searches using this operator don’t
change.all_of— a composite operator whose conditions are each matched independently, by their own array entry. Use it for compound cross-entry queries, like a role at one company and a degree from another school, or a past job plus a current one. See the operator reference.has_all— shorthand for one field with several values (“worked at both A and B”). It expands to oneall_ofblock per value.- Works across person history — employment, education, certifications, and honors.
- Combines with document-level filters — nest
all_ofinside a plainandto add profile-level conditions, so you can ask for a role at a company and a person based in a given country in one query. - One matching entry per condition — each condition is satisfied by at least one entry in the array. Conditions do not have to land on the same entry, and they do not have to cover every entry.
- Backward compatible — a plain
andon one array path still matches within a single entry, so existing queries return exactly what they returned before. - Pricing — included on all plans. These are new operators on an existing
endpoint, so there is no new credit cost and the
/person/searchrate limit is unchanged.
POST /person/search with x-api-version: 2025-11-01, for all
users, at no extra cost.Job Search: geo_distance and geo_exclude on job location
Job Search now filters postings by true
geographic radius. Pick a point — either a place name like “San Francisco” or a
pair of coordinates — and set a distance. geo_distance then returns every job
posting whose location falls within that distance of the point, instead of
matching on exact location text and listing city names one at a time.geo_distance— returns job postings whose location falls inside the radius.geo_exclude— the inverse ofgeo_distance. It returns job postings whose location falls outside the radius and excludes the ones inside it, so you can skip a region you already cover. See the geographic radius filters reference.- Setting the point and the distance — the point is what the radius is
measured from. Pass it as
location(a place name, geocoded server-side) or aslat_lng([lat, lng], which skips geocoding), then add adistanceand aunitsuch askmormi. - Either field path — use
location.raworlocation; both target the same geo point. - Geocoded on ingest — each posting’s location text is turned into real coordinates when Crustdata adds the posting to the database, so the filter compares coordinates rather than matching location strings, and postings in towns you never thought to list still come back.
- Pricing — no new credit cost. A radius query bills the same as any other
/job/searchcall, and the endpoint’s rate limit is unchanged.
POST /job/search with x-api-version: 2025-11-01, for all users,
at no extra cost.Teams: one workspace with shared credits, API keys, and roles
Bring your whole team into one Crustdata account. Invite teammates by email, give each one a role, and work from a shared credit pool and one set of API keys. Credits and keys now belong to the team, so everyone works from the same place instead of a separate login each. Free on every plan, and existing accounts were migrated automatically.- Every account became a team - your existing account is now a team named Personal with you as the owner. Nothing changes until you invite someone.
- Invite by role - Owner (the creator; bypasses every permission check and cannot be reassigned), Admin (invites members, buys credits, manages payment methods, and creates and manages API keys, watchers, and integrations), and Member (runs playground searches and enrichments against team credits and views usage, keys, watchers, exports, and billing, but cannot purchase, manage keys, or change the team).
- Shared credits and API keys - everyone on a team draws from one credit pool
and one set of
cd_keys, gated by role. Rate limits are shared across the team. - Team switcher - one person can belong to and create several teams and switch between them from the sidebar. Credits never move between teams: each team buys and holds its own, which is a clean way to cap spend for a project, client, or department.
- Per-team billing - each team has its own wallet, subscription, invoices, and saved cards. A new team starts at 0 credits and buys its own.
- Default limits - up to 2 teams per user and 10 members per team. Reach out to sales to raise either limit.
Per-API-key limits: cap credits and restrict endpoints on one key
A workspace admin can now attach two independent limits to an individual API key from the dashboard API Keys page, without affecting the rest of the account.- Monthly credit cap — cap how many account credits one key can spend per
month; it resets on the 1st (UTC). Use it for a key you hand to a teammate, a
customer, or an unattended job. When the cap is reached, that key returns
402witherror.typeinsufficient_credits. Setting the cap to0stops the key until an admin raises it. See Per-key monthly limit. - Endpoint access — restrict a key to a subset of the endpoints the account
has enabled. A call to an endpoint outside that subset returns
403witherror.typepermission_error. See Per-key endpoint access.
error.type as their account-level
equivalents, so clients that already handle account-level 402 and 403 need
no new code — only the message differs, naming the key’s limit so you can tell
which one was hit. Handling patterns are in
Best practices.Usage and logs: see and filter activity by API key
Your dashboard now records which API key made each request, so you can tell your keys apart in usage and request logs instead of seeing one merged total.- Per-key attribution — every request is tagged with the key that made it, including requests from keys you have since deleted, so past activity stays attributed after a key is rotated out.
- Filter by key — narrow the Usage and Logs views to one or more keys to see exactly what each key spends and calls. Together with per-key credit caps and endpoint access, you can set a key’s limits and then confirm what it actually did.
Technographics: filter by and enrich a company’s technology stack
Crustdata now exposes the technologies detected for a company across Company Enrich, Company Search, and Company Autocomplete.technographicsfield group on Enrich — request it explicitly viafields(it is never included by default) to gettotal_technologies,top_technologies, the fulltechnologieslist (each entry withname, detectionsources,posting_count, catalogcategories, adescription, andevidence), andupdated_at. See the field reference.- Search filters — filter on
technographics.total_technologies(also sortable),technographics.top_technologies,technographics.technologies.name,technographics.technologies.category, andtechnographics.technologies.super_category. Technographics values are not returned in search responses — filter, then enrich. See searchable fields. - Autocomplete — two new company-scope fields:
technology(technology names) andtechnology_category(category slugs) for discovering valid filter values. - Access and pricing —
technographicsrequires field-level permission on your account. It is a billed add-on: +2 credits per company that returns technographics data, on top of the base enrich cost; companies with no technographics data are not charged the add-on. The same add-on applies to Batch Company Enrich.
POST /company/enrich, POST /batch/company/enrich,
POST /company/search, and POST /company/search/autocomplete with
x-api-version: 2025-11-01.Company lookup: more accurate domain matching for large organizations
We improved domain matching in Company Identify and Company Enrich, especially for large organizations and companies with subsidiaries. On these domains a lookup could sometimes surface a related record (a subsidiary, a regional arm, an acquired company, or a showcase page) instead of the parent company. Matching now favors the primary company, automatically.- Better matches for large organizations - a domain maps to the parent
company rather than a subsidiary, an acquired company, or a showcase page. For
example,
amazon.comresolves to Amazon,paypal.comto PayPal, andschwab.comto Charles Schwab. - Nothing to change - same endpoints, same request shape, and same response shape. No new fields and no confidence score to handle; matches just get better automatically.
- Biggest gains on high-volume domains - in testing across Fortune 500 US companies, top-1 accuracy by domain rose from 88% to 94%.
POST /company/identify and POST /company/enrich with
x-api-version: 2025-11-01. No changes to the legacy identity endpoints.Person Search: natural-language search.query (beta)
Person Search adds search.query, a
natural-language query that returns a ranked list of people instead of requiring
you to hand-build every filter. Crustdata pulls the hard constraints out of the
query and applies them as exact filters, semantically matches the rest against
the full profile, and orders results by fit.search.query- describe who you want in plain language; results are ranked by profile context (title, skills, company history, education, location, summary).search.mode-hybrid(default, blends keyword and concept matching) andsemantic(concepts). For exact keyword matching, see the keyword search changelog below.mode- two recall modes:managed(default; the query may add constraints) andexact(your filters are hard constraints and the query only ranks within them).fit- each profile returns a relevance tier (strong/possible/weak) alongsidetotal_count_relation, so you can keep only high-confidence matches.- Not included - sorts; semantic results come back already rank-ordered by relevance.
- Pricing - Person Search parity at 0.03 credits per result, no premium for semantic ranking, and the same 30 requests/minute rate limit.
Person Search: keyword search with Boolean operators
Person Search now lets you search for keywords across the full profile. Setsearch.mode: "lexical" to match on
keywords, so exact terms, acronyms, names, and IDs are found wherever they
appear in the profile (title, skills, company history, education, location,
summary).search.mode: "lexical"- keyword matching across the whole profile, for when you want exact terms to be found rather than concept ranking.search.query_syntax: "boolean"- read the query as a Boolean expression instead of plain text: a space means AND (every term required),|means OR, and you can combine terms for precise keyword search. Honored only withsearch.mode: "lexical"and top-levelmode: "exact".
Multiple API keys on one account
Every Crustdata account used to have a single API key. You can now create and manage several named keys on one account, so you can keep dev and prod apart, give each project or teammate its own key, and switch off a key without affecting the others. Works across the API and MCP. Every existing key was migrated automatically and named Default.- Create and name keys - add as many keys as you need at
app.crustdata.com/api-keys, rename them
anytime, and copy the generated
cd_key into any API or MCP request. See Authentication. - Per-key status -
ACTIVE(usable across the API and MCP),INACTIVE(blocked instantly and reversible), andDELETED(permanently retired; the account’s other keys keep working). - Shared credits and rate limit - all keys on an account draw from one credit pool and one rate-limit bucket today, so keys separate and organize work rather than cap spend or speed per key.
- Coming next - credit balance and limits set at the individual API-key level.
Person Contact Enrich: business emails, personal emails, and phone numbers
Person Contact Enrich is a new endpoint that returns contact data for up to 25 people in a single request. Supplyprofessional_network_profile_urls or business_emails as input and
get business emails, personal emails, and phone numbers back in the same
response — no polling.- Identifiers — pass exactly one of
professional_network_profile_urls(a profile URL) orbusiness_emailsto reverse-look-up a person, up to 25 values per request. fields— select which contact data to return:contact.business_emails,contact.personal_emails,contact.phone_numbers, andcontact.websites(orcontactfor all). Each email entry includes its deliverabilitystatus. See the contact fields reference.- Response — the same record shape as
/person/enrich, restricted to theperson_data.contactobject. For higher fill rate on bulk lists, use the async Batch Contact Enrich job instead.
POST /person/contact/enrich with x-api-version: 2025-11-01.Person Search: geo_exclude filter operator
Person Search adds a geo_exclude filter
operator — the inverse of geo_distance. It excludes profiles inside a
radius and keeps everyone else, so you can carve out a metro you already cover
or target candidates outside a region.geo_excludetakes the same value object asgeo_distance: a centre given aslocation(geocoded server-side) orlat_lng([lat, lng], skips geocoding), a requireddistance, and an optionalunit(km,mi,miles,m,meters,ft,feet; defaults tokm). When bothlocationandlat_lngare supplied,lat_lngwins. See the operator reference and a worked example.
POST /person/search with x-api-version: 2025-11-01.Professional-network name and normalized title on Person profiles
Person Search and Person Enrich now return additionalbasic_profile identity fields.basic_profile.professional_network_name— the display name on the person’s professional-network profile, returned by both/person/searchand/person/enrich. On search it is also a filterable field (not sortable). See the search field reference and the enrich response reference.basic_profile.normalized_title— the normalized job-title object (matched_title,department,sub_department,similarity,confident) is now also returned by/person/enrich, matching the field already available on/person/search.
POST /person/search and POST /person/enrich with
x-api-version: 2025-11-01.Normalized titles, education details, and logos on Person Search
Person Search results now include a normalized job-title classification, richer education entries, and stable Crustdata-hosted logo permalinks for employers and schools.basic_profile.normalized_title— an object withmatched_title,department,sub_department,similarity, andconfident. Filter onbasic_profile.normalized_title.matched_title,.department, or.sub_department(filterable, not sortable). See the field reference.- Education location and description — each
education.schools[]entry now returns alocationobject (raw,city,state,country,continent) and adescription. The location sub-fields are filterable viaeducation.schools.location.*. See the education fields. education.schools[].institute_logo_permalink— a stable Crustdata-hosted school logo URL, returned for display.experience.employment_details[].company_profile_picture_permalink— a stable Crustdata-hosted employer logo URL, so you can render company logos without resolving image URLs yourself. See the profile-card example.
POST /person/search with x-api-version: 2025-11-01.Follower count on Person Search profiles
Person Search now exposes afollowers
field on each result’s professional_network block, alongside the existing
connections field.professional_network.followers— integer follower count from the profile. Filterable and sortable: pass it infilters.conditions[].fieldorsorts[].field. See the filter and sort field reference.
POST /person/search with x-api-version: 2025-11-01.Person Search: new filter operators
Person Search gains two new filter options for the2025-11-01 API:(!)— fuzzy negation. Excludes profiles whose value contains the given substring (case-insensitive). Multi-word values are matched as a literal phrase, so(!) "New York"excludes profiles literally containing"New York"but not"New Yorker". To exclude on each word independently, send separate(!)conditions inside anandgroup. See the operator reference and a worked example.geo_distanceacceptslat_lng. Supply explicit coordinates as[lat, lng]to skip geocoding. When bothlocationandlat_lngare provided,lat_lngwins. See thegeo_distancereference and thelat_lngexample.
POST /person/search with
x-api-version: 2025-11-01.🚀 New API version: 2025-11-01
We are launching a new, versioned API that replaces the legacy
/screener and /data_lab endpoints. Every product — Company, Person,
Job, Web, and Social Post — now lives under a consistent, purpose-built
surface area.What’s new
- Versioned endpoints. Every call requires the
x-api-version: 2025-11-01header, so future changes ship without breaking existing integrations. - Bearer authentication.
Authorization: Bearer <key>replaces the legacyTokenscheme across every endpoint. - Structured error envelope. A consistent
{ error: { type, message, metadata } }shape across every endpoint. - Cursor-based pagination. Opaque
next_cursorreplaces numericoffseton search endpoints. - Match-result envelope for enrich. Enrich responses now return
matches[]with aconfidence_scoreper match. - Nested, neutral response sections. Flat top-level keys are grouped
under sections like
basic_info,employee_reviews, andfunding.
Migrating from the legacy API
Every legacy/screener and /data_lab endpoint has a current
replacement. The Migration guides map each
legacy endpoint to its new equivalent and walk through request keys,
field renames, type changes, and response-shape differences.
