[{"date":"2026-10-20","type":"breaking","title":"`/v2/search/waterfall-icp-keyword` takes a `location` object","body":"`location` in each `cascade` item of `/v2/search/waterfall-icp-keyword` is now an object with `city`, `country_code`, `continent` and `sales_region`, the same filters as `people.location` on `/v2/search/people`; a person must match every field you set. The old list of country codes still works. Each list in `location` accepts at most 10 values (previously 50 for country codes), and a longer list returns `422`. Cut any `location` list to 10 values or fewer.","affected_endpoints":["/v2/search/waterfall-icp-keyword"]},{"date":"2026-10-16","type":"announcement","title":"MCP sign-in now asks for `blitz:read` and `offline_access`","body":"`POST /mcp` without credentials now answers `401` with a `WWW-Authenticate` header that names `scope=\"blitz:read offline_access\"` and carries no `error` code. A call with a bad token keeps `error=\"invalid_token\"` and names the same scope. MCP clients that follow the scope get a refresh token, so sessions renew without a new sign-in. Access with the `x-api-key` header is unchanged."},{"date":"2026-10-16","type":"breaking","title":"MCP `max_results` is capped on three people tools","body":"The MCP server now rejects `max_results` above 25 on `people_search` and `people_employee_finder`, and above 50 on `people_waterfall_icp` (previously 50 and 100). A full page of profiles at the old maximum was larger than many MCP clients accept in one tool result. The REST endpoints keep their limits and the default stays 10. Lower `max_results` in any MCP call that asks for more, and read the rest with `cursor` (`people_search`) or `page` (`people_employee_finder`)."},{"date":"2026-10-15","type":"feature","title":"`/v2/enrichment/company` returns the MX records of the company domain","body":"`/v2/enrichment/company` now returns `mx` on the company: the MX records of its `domain`, each with a `priority` and a `host`, lowest priority first, at most 10. `mx` is `null` when the domain has no MX data yet and an empty list when the domain takes no mail. The other fields are unchanged.","affected_endpoints":["/v2/enrichment/company"]},{"date":"2026-10-15","type":"feature","title":"Exact match with `[brackets]` on job titles and company HQ","body":"Wrap a phrase in square brackets, such as `[Head of Sales]`, to match the whole value exactly, ignoring case and accents. This works in the `include` and `exclude` lists of `job.title` on `/v2/jobs/search`, `/v2/jobs/company` and `/v2/company/tam-by-jobs`, and of `company.hq.city` and `company.hq.state` on `/v2/jobs/search` and `/v2/company/tam-by-jobs`. A phrase without brackets matches as before, so existing calls are unchanged.","affected_endpoints":["/v2/jobs/search","/v2/jobs/company","/v2/company/tam-by-jobs"]},{"date":"2026-10-15","type":"feature","title":"`/v2/search/waterfall-icp-keyword` can now search past jobs","body":"`/v2/search/waterfall-icp-keyword` takes two new booleans in each `cascade` item, `include_active_jobs` (default `true`) and `include_past_jobs` (default `false`). Set `include_past_jobs` to `true` to find people by a job they held before at the company; the company and the title filters of the cascade item must then hold in the same job, and `experiences` carries that job with `job_is_current` set. A person is returned once, `is_recent_hire` in the cascade item applies to the jobs the two booleans select, and a cascade item that sets both booleans to `false` returns a `422`.","affected_endpoints":["/v2/search/waterfall-icp-keyword"]},{"date":"2026-10-14","type":"feature","title":"Filter companies by mail provider with `company.mx`","body":"`/v2/search/people`, `/v2/company/tam-by-people` and `/v2/search/companies` take `company.mx.provider` with `include` and `exclude` lists of mail providers, such as `google` or `microsoft`. A company matches when any of its domains uses the provider, and `any` stands for every provider: in `include` it keeps companies with mail data, in `exclude` it keeps companies with none. Companies with no mail data are never matched by `include`, and `exclude` never drops them unless you use `any`.","affected_endpoints":["/v2/search/people","/v2/company/tam-by-people","/v2/search/companies"]},{"date":"2026-10-14","type":"feature","title":"Filter people by education start year and end year","body":"`/v2/search/people` and `/v2/company/tam-by-people` take `people.education.start_year` and `people.education.end_year`, each a range with `min` and `max` years (`0` leaves a bound unset). A range applies to the same education entry as `people.education.include`, and an entry with no such year never matches a bound. A range where `min` is greater than `max` returns a `422`.","affected_endpoints":["/v2/search/people","/v2/company/tam-by-people"]},{"date":"2026-10-14","type":"feature","title":"`is_recent_hire` finds people who started a job recently","body":"Set `people.is_recent_hire` to `true` on `/v2/search/people` and `/v2/company/tam-by-people` to keep only people with a job that started in the last 90 days; the job must also match your other job and company filters, and a job with no start date never matches. `/v2/search/employee-finder`, `/v2/enrichment/company-distribution-by-country` and `/v2/enrichment/company-distribution-by-department` take the same top-level flag and apply it to the current job at the company, and `/v2/search/waterfall-icp-keyword` takes it in each `cascade` item. The default is `false`, so existing calls are unchanged.","affected_endpoints":["/v2/search/people","/v2/company/tam-by-people","/v2/search/employee-finder","/v2/search/waterfall-icp-keyword","/v2/enrichment/company-distribution-by-country","/v2/enrichment/company-distribution-by-department"]},{"date":"2026-10-14","type":"feature","title":"`/v2/search/people` can now search past jobs","body":"`/v2/search/people` takes two new booleans in the `people` object, `people.include_active_jobs` (default `true`) and `people.include_past_jobs` (default `false`). Set `people.include_past_jobs` to `true` to find people by a job they held before; your job and company filters must then hold in the same job, and `experiences` carries that job with `job_is_current` set. A person is returned once, and a request that sets both booleans to `false` returns a `422`.","affected_endpoints":["/v2/search/people"]},{"date":"2026-10-14","type":"improvement","title":"Country distribution now counts people, not jobs","body":"`/v2/enrichment/company-distribution-by-country` now counts people instead of jobs, so `count` and `total_employees` no longer include a second job held by the same person at the company (previously they counted jobs). `/v2/enrichment/company-distribution-by-department` is unchanged.","affected_endpoints":["/v2/enrichment/company-distribution-by-country"]},{"date":"2026-09-28","type":"breaking","title":"Unknown/invalid fields in a request body now return `422`","body":"Every `/v2` endpoint now rejects a JSON request body that contains a field it does not define, at any depth, with a `422` that names the field and where it is (previously the field was dropped and the call ran without it). A misspelled filter such as `company.industry.includes` no longer returns results as if the filter were not there. Remove or correct any field your client sends that the endpoint documentation does not list."},{"date":"2026-09-28","type":"breaking","title":"`422` errors now name every field and suggest a fix","body":"A `422` from any `/v2` endpoint now carries the same `success` and `message` fields as every other error, plus an `errors` list: each item gives the full `field` path, such as `company.industry.include[0]`, and a `message` that suggests the closest valid field or value when one exists. `message` sums up the first three errors, and the response no longer repeats your request or lists every allowed value. A request body that is not valid JSON now returns a `400` with a JSON `message` instead of plain text. Update any client that reads `type`, `on`, `property`, `found` or `errors[].path` from a `422`."},{"date":"2026-09-28","type":"improvement","title":"Company distribution endpoints now cost 1 record on success","body":"`/v2/enrichment/company-distribution-by-country` and `/v2/enrichment/company-distribution-by-department` now cost 1 record on success (previously 1 record per call). Both responses now carry a `found` field. A call returns `found: false` and costs 0 records when the company is unknown or has no current employees.","affected_endpoints":["/v2/enrichment/company-distribution-by-country","/v2/enrichment/company-distribution-by-department"]},{"date":"2026-09-21","type":"improvement","title":"`/v2/search/people` returns only the position that matched","body":"`experiences[]` on `/v2/search/people` now carries the single position that matched your search, allowing to understand what matched.","affected_endpoints":["/v2/search/people"]},{"date":"2026-09-17","type":"improvement","title":"`Unknown` now also matches records with no company","body":"`Unknown` in `company.industry.include` now returns people and job postings that have no company attached, next to those whose company has no industry value (previously it returned only the second group). In `exclude` it drops both. `/v2/search/companies` is unchanged: `Unknown` there still means a company with no industry value.","affected_endpoints":["/v2/search/people","/v2/jobs/search"]},{"date":"2026-09-16","type":"fix","title":"`min` above `max` in a range filter now returns `422`","body":"A range filter sent with `min` greater than `max` was accepted: on `company.revenue` the call failed with a `500`, and on every other range it returned no results. Any range filter whose `min` is above its `max` is now rejected with a `422` that names the field. A `max` of `0` still means no upper bound.","affected_endpoints":["/v2/search/people","/v2/search/companies","/v2/jobs/search","/v2/company/tam-by-jobs","/v2/company/tam-by-people"]},{"date":"2026-09-16","type":"feature","title":"`Unknown` filters companies with no industry","body":"The `company.industry` filter now accepts `Unknown`, which matches companies that have no industry value. In `include` it is added to the industries you list, so `[\"Banking\", \"Unknown\"]` returns banks plus every company with no industry; in `exclude` it drops them. Before this, reaching those companies meant listing every other industry in `exclude`, which the 50-entry cap made impossible.","affected_endpoints":["/v2/search/people","/v2/search/companies","/v2/jobs/search","/v2/company/tam-by-people","/v2/company/tam-by-jobs"]},{"date":"2026-09-15","type":"improvement","title":"More contact data, and more accurate contact data","body":"Contact coverage is up 40% and contact accuracy is now above 90%. On the same queries you run today you should see fewer empty and fewer stale `email` and `phone` values."},{"date":"2026-09-15","type":"breaking","title":"Three changes to `field_of_study`, `profile_picture_url` and `headline`","body":"Three changes to the person returned by every people endpoint: `education[].field_of_study` is removed and the field of study is now part of `degree`, for example `Bachelor of Science, Industrial Engineering`; `profile_picture_url` is now always `null`, though it stays in the response so clients do not break; and `headline` is now built from the person's first position as `<job title> | @<employer>` rather than the free-text headline written on the LinkedIn profile. Update any client that reads `field_of_study` or `profile_picture_url`.","affected_endpoints":["/v2/search/people","/v2/search/employee-finder","/v2/search/waterfall-icp-keyword","/v2/enrichment/person","/v2/enrichment/email-to-person","/v2/enrichment/phone-to-person"]},{"date":"2026-09-15","type":"improvement","title":"New fields on the person output","body":"People now carry `location.postal_code` and `location.street_address`, and every entry in `experiences[]` now carries `job_contract_type` and `job_work_arrangement`. `experiences[].company_domain` is now filled on past positions as well as the current one, and `experiences[].company_name` now prefers the name on the linked LinkedIn company page.","affected_endpoints":["/v2/search/people","/v2/search/employee-finder","/v2/search/waterfall-icp-keyword","/v2/enrichment/person","/v2/enrichment/email-to-person","/v2/enrichment/phone-to-person"]},{"date":"2026-09-15","type":"improvement","title":"`/v2/search/people` and `/v2/enrichment/person` return the whole career","body":"`/v2/search/people` returned only the position that matched your search in `experiences[]`, and `/v2/enrichment/person` returned only current positions. Both now return every position a person has held, in profile order.","affected_endpoints":["/v2/search/people","/v2/enrichment/person"]},{"date":"2026-09-15","type":"feature","title":"Added a LinkedIn profile enrichment endpoint","body":"You can now enrich a person from their LinkedIn profile URL at `POST /v2/enrichment/person`. It returns their whole career, education, skills and certifications, and costs 1 record on success.","affected_endpoints":["/v2/enrichment/person"]},{"date":"2026-09-15","type":"improvement","title":"Education entries carry a school name more often","body":"Some education entries came back with an empty `education[].school_name`. Fewer entries are now missing the school name.","affected_endpoints":["/v2/search/people","/v2/search/employee-finder","/v2/search/waterfall-icp-keyword","/v2/enrichment/person","/v2/enrichment/email-to-person","/v2/enrichment/phone-to-person"]},{"date":"2026-09-15","type":"improvement","title":"`/v2/enrichment/email` no longer repeats the same address","body":"`profile[]` could return the same address more than once, with every field identical. Duplicate entries are now returned once.","affected_endpoints":["/v2/enrichment/email"]},{"date":"2026-09-15","type":"improvement","title":"The company revenue filter accepts larger values","body":"`company.revenue.min` and `company.revenue.max` rejected values above a ceiling that ruled out the largest companies. Both now accept any revenue figure you need.","affected_endpoints":["/v2/search/companies","/v2/search/people","/v2/company/tam-by-people"]},{"date":"2026-09-15","type":"fix","title":"`/v2/enrichment/domain-to-linkedin` returns the right company more often","body":"`/v2/enrichment/domain-to-linkedin` could return a company that was not the owner of the domain you sent. Some domains now return a different and more accurate company.","affected_endpoints":["/v2/enrichment/domain-to-linkedin"]},{"date":"2026-09-15","type":"fix","title":"`null` now reads the same as leaving a field out","body":"Every `/v2` request body now treats `null` on an optional field exactly like an omitted field, so a client that serialises its unset filters as `null` no longer gets a `422`. Defaults still apply: `country_code: null` on `/v2/search/employee-finder` falls back to `[\"WORLD\"]`, and `max_results: null` falls back to the endpoint default. This holds inside nested filter objects too. A `null` on a required field is still rejected, and so is a `null` inside a list."},{"date":"2026-09-15","type":"breaking","title":"Search filter lists are capped at 50 entries","body":"Every filter list on the search endpoints now accepts at most 50 entries, and a longer list returns `422`. This covers the `include` and `exclude` arrays on the company and people filters, the job and firmographic filters, and the industry, location and code lists. On `/v2/search/waterfall-icp-keyword` the `cascade` array is capped at 10 steps, and each step still accepts up to 50 title phrases. A very long exclusion list used to time out and return `500`, so split a longer list across several requests.","affected_endpoints":["/v2/search/people","/v2/search/companies","/v2/search/employee-finder","/v2/search/waterfall-icp-keyword","/v2/jobs/search","/v2/jobs/company","/v2/company/tam-by-jobs","/v2/company/tam-by-people"]},{"date":"2026-09-11","type":"feature","title":"Added the TAM By People endpoint","body":"You can now build a target-account list from the people inside companies at `POST /v2/company/tam-by-people`. Give it your people and company filters and it returns the distinct companies whose current employees match, each with a `matched_people` count. It costs 1 record per result.","affected_endpoints":["/v2/company/tam-by-people"]},{"date":"2026-09-11","type":"breaking","title":"`/v2/search/people` no longer accepts `people.linkedin_url`","body":"A request that still sends the `people.linkedin_url` filter succeeds, but the filter is ignored, so you get results for your other criteria instead of the people you asked for. `/v2/company/tam-by-people` still accepts it. Check any saved query that used it.","affected_endpoints":["/v2/search/people"]},{"date":"2026-09-02","type":"announcement","title":"The Python and JavaScript SDKs now expose the `fair_usage` block","body":"`blitz-api-py` and `blitz-api-js` are in sync with the current API. Every response model now carries `fair_usage`, so you can read `records_used`, `records_remaining`, `next_reset_at`, `rate_limit` and `request_id` straight off the typed response instead of parsing headers. Upgrade to the latest SDK version to get it.","links":[{"label":"blitz-api-py on PyPI","url":"https://pypi.org/project/blitz-api-py"},{"label":"blitz-api-js on npm","url":"https://www.npmjs.com/package/blitz-api-js"}]},{"date":"2026-09-02","type":"improvement","title":"Legacy plans now run at 50 requests per second","body":"Every plan created before 30 September 2026 now runs at 50 requests per second, at no extra cost. The new limit is already live on your key. `fair_usage.rate_limit.requests_per_second` reports the limit currently applied to you."},{"date":"2026-09-01","type":"breaking","title":"`remaining_credits` on the key-info endpoint is renamed `records_remaining`","body":"`GET /v2/account/key-info` now returns your balance under `records_remaining` instead of `remaining_credits`. The new name matches `fair_usage.records_remaining` and the `x-records-remaining` header. Update any client that reads `remaining_credits`.","affected_endpoints":["/v2/account/key-info"]},{"date":"2026-08-31","type":"breaking","title":"The `x-credit-*` response headers are replaced by `x-records-*`","body":"`x-credit-cost` and `x-credit-remaining` are removed. Every `/v2` response now returns `x-records-used` and `x-records-remaining` instead, carrying the same values. Update any client that reads the old header names."},{"date":"2026-08-31","type":"improvement","title":"Every response now reports your record usage and rate limit","body":"Every `/v2` endpoint now returns a `fair_usage` block on success, and on the `402` insufficient-balance error. `fair_usage.records_used` gives the records this request consumed, `fair_usage.records_remaining` the records left on your plan and `fair_usage.next_reset_at` when your balance resets. `fair_usage.rate_limit` gives `requests_per_second` and `remaining_this_second`, and is absent on `/v2/account/key-info`, which is not rate limited. `fair_usage.request_id` gives an id you can quote to support."},{"date":"2026-08-28","type":"improvement","title":"Reverse phone lookup now costs 1 record","body":"A successful `/v2/enrichment/phone-to-person` reverse phone lookup now costs 1 record on success (previously 5).","affected_endpoints":["/v2/enrichment/phone-to-person"]},{"date":"2026-08-13","type":"announcement","title":"Added TAM By Jobs and the changelog endpoint to the SDKs","body":"`blitz-api-js` and `blitz-api-py` SDKs now expose `company.tam_by_jobs()` - build a Total Addressable Market of companies from live hiring signals - and `changelog.list()` to fetch this changelog."},{"date":"2026-08-12","type":"breaking","title":"TAM By Jobs `min_per_company` maximum lowered to 25","body":"The `job.min_per_company` floor on `/v2/company/tam-by-jobs` now accepts a maximum of 25 (previously 100). Requests passing a higher value are rejected. Lower it to 25 or below.","affected_endpoints":["/v2/company/tam-by-jobs"]},{"date":"2026-08-06","type":"fix","title":"Company search cursor now returns every matching company","body":"When paging through `/v2/search/companies` with `cursor`, the walk could stop well short of `total_results`, skipping matching companies that were never returned across the full set of pages. Cursor pagination now covers the complete result set, so you receive every company that matches your filters.","affected_endpoints":["/v2/search/companies"]},{"date":"2026-08-05","type":"improvement","title":"Search pages now reliably return the full result count","body":"Company and job search pages now fill to your requested `max_results`, so pages no longer come back short. If the search has a partial failure, these endpoints now return `503` (retriable) instead of a truncated page that looks like the end of results.","affected_endpoints":["/v2/search/companies","/v2/jobs/search","/v2/jobs/company"]},{"date":"2026-08-05","type":"improvement","title":"TAM By Jobs 50k limit now counts returned companies","body":"The 50,000-company pagination cap on TAM By Jobs now counts companies actually returned to you.","affected_endpoints":["/v2/company/tam-by-jobs"]},{"date":"2026-08-05","type":"feature","title":"Added TAM By Jobs endpoint","body":"You can now find companies from their job offers on Linkedin at `POST /v2/company/tam-by-jobs`.","affected_endpoints":["/v2/company/tam-by-jobs"],"links":[{"label":"Docs","url":"https://docs.blitz-api.ai/api-reference/company-search/tam-by-jobs"}]},{"date":"2026-08-05","type":"feature","title":"Added a public changelog endpoint","body":"You can now fetch the Blitz API changelog at `GET /changelog`, with no API key required. Use `days` to limit to recent changes and `limit` to cap the count.","affected_endpoints":["/changelog"]}]