Skip to main content
Resources

Product updates

All the latest updates, improvements, and fixes to TheirStack

September 10, 2026

Skip the records a webhook missed while it was off

A webhook keeps track of what its saved search matches even while it is disabled. Until now, turning it back on delivered all of it: every record matched during the pause was sent and charged. After a short pause you did not mean to leave, that is what you want. After a long one, or when you disabled it precisely to stop the feed, it is a surprise burst of events and credits. Now you choose.

In the app

Flip a disabled webhook back on and a dialog opens before anything is sent:

A disabled webhook, with the status toggle set to Inactive

The enable dialog, with two options: Deliver the missed records (the default, sent as soon as you enable the webhook and charged as usual, what webhooks did before) or Skip them (the new option, highlighted, nothing sent or charged and the feed starts from the moment you enable it)

In the API

Enabling a webhook is PATCH /v0/webhooks/{id}/status with is_active: true. Pass skip_events_while_inactive: true alongside it to start the feed from that moment instead of replaying the backlog:

{
    "is_active": true,
    "skip_events_while_inactive": true
}

skip_events_while_inactive defaults to false, so a call that only sends is_active: true behaves exactly as before: the webhook delivers everything matched while it was disabled. You opt into skipping by sending true, and nothing changes for existing integrations that never send the field. Sending it on a disable (is_active: false) is ignored; the choice is read when you enable.

With MCP

update_webhook_status on our MCP server enables and disables a webhook, and honours the same choice. Tell an assistant connected to your workspace to turn a webhook back on without replaying what it missed, and it starts the feed from now.

September 8, 2026

Now you can filter companies by when you revealed them

If you run weekly or monthly lead pulls in TheirStack — revealing companies in batches and pushing them to Apollo, a CRM, or a spreadsheet — you've probably wanted to go back and ask "give me the companies I revealed between these two dates" without rerunning the original search.

You can now. There's a new Company Reveal state filter on both company search and job search. It gives you three choices: keep only companies you've never revealed, keep only ones you've revealed at some point, or pick Revealed between dates to narrow to the ones you revealed inside a window you choose.

In the API, this maps to two new filters on /v1/companies/search and /v1/jobs/search:

  • company_revealed_at_gte — only include companies you revealed at or after this timestamp
  • company_revealed_at_lte — only include companies you revealed at or before this timestamp

Both are team-scoped — they use the reveal history of the calling team, so the filter only narrows to companies you've actually revealed.

Why we built this

A few customers told us they run the same search week after week with the reveal filter set to exclude companies they'd already seen, export the result, and push it to Apollo. The catch: as soon as you scroll through the results table and click into companies, those get marked as revealed, so by the time you hit Export the filter has shrunk what comes back. The export ends up empty or near-empty.

With this filter, the workflow becomes:

  1. Do your pull however you like — search, browse, reveal.
  2. When you want to act on that batch, search again with Company Reveal stateRevealed between dates set to the window of that pull (e.g. last 7 days).
  3. Add the results to a list, or export them straight to CSV / push to Apollo.

The "revealed in the last week" set is now something you can ask for directly, instead of reconstructing it by holding the results page open and hoping nothing scrolls.

What you need to do

Nothing. The filter is live in the app under the Company group, and the two API fields are documented in the company search and job search references.

See what spends your credits: API, app, webhooks and MCP

When your credit consumption goes up, the first question is where it came from. Settings → Usage now opens with one daily chart of credits by delivery method, and lets you follow any of them down to the calls that produced them without leaving the page.

Start from the overview

The overview stacks each day's credits by delivery method: API, app, webhooks and MCP. API and Company credits stay separate, because they are different units and adding them together would tell you nothing. Consumption we cannot attribute to a delivery method is still shown rather than dropped from the total, so the chart adds up to your bill.

Each card takes you to the section below with that origin already selected, so one click lands you on the requests or the webhook events behind the number.

Daily credits stacked by delivery method, with a card per method

Requests are listed one by one, and you can switch the chart between request counts and credits. Breakdown by groups them by search pattern, status, endpoint or origin.

A search pattern is a set of requests that sent the same stable filters, so a saved search you run every morning reads as a single line with its total cost instead of thirty rows. Open a pattern to see the normalized filters behind it and drill into the original requests, each with its duration, credits, status and returned records, still on the same page.

Filters only offer values that actually occur in your workspace, and every option shows its total for the metric you are looking at, largest first. So you pick from what happened, rather than guessing at endpoint names.

Requests broken down by search pattern, measured in credits

Webhooks

Webhooks get the same treatment: events or credits, broken down by webhook, event type or status, so you can see which one accounts for the period. A charge split across several ledger entries still counts as one event, so the counts and the credits agree.

Webhook events broken down by webhook

Usage where you configure it

The API keys page and the MCP page now carry the same chart, scoped to that channel. The MCP page also breaks usage down per tool, so you can see what search_jobs and search_companies each cost you, and open the request log narrowed to one tool's calls.

Per-tool usage on the MCP page, with calls and credits per tool

Share an investigation

Dates, filters, breakdown, metric, selected series and page all live in the URL. Bookmark a spike or send the link to a teammate and they open exactly what you were looking at.

In the API

GET /v0/requests/ lists individual requests with their duration, api_credits and ui_credits. GET /v0/requests/analytics returns daily counts and credits, with group_by set to search_pattern, endpoint, origin or status. GET /v0/requests/facets lists the values you can filter on. For spending by charge date, use GET /v0/teams/credits_consumption.

The two follow different clocks, which is worth knowing before you reconcile them: the overview follows the date the credits were charged, while requests and webhooks follow when the request ran or the event was created, including charges that landed later. Totals for the same period can differ for that reason, so compare matching cohorts.

With MCP

Ask your assistant which searches used the most credits this week and how long their requests took. The usage tools get_requests, get_request_analytics and get_teams_credits_consumption read the same history, through the same authentication as the API, for the workspace you are connected to.

September 7, 2026

Cap how much a webhook sends and spends each day

A webhook sends every record that matches its saved search. Point one at a broad search, or at the whole history of a search instead of only new records, and the credits add up fast. Now you can put a ceiling on it.

Set a daily event limit (max_events_per_day) on any webhook. Once it has sent that many events in a day, it stops until midnight UTC. An event stopped by the limit is never sent and never charged, so the cap bounds what the webhook costs you, not only what it delivers.

In the app

When you set a limit, the form shows how many records the search has matched per day over the last week, and warns you if your cap sits below that, with how many records a day you would miss. You can still save it: getting less than everything is a normal thing to want, and the warning is there so it is a decision rather than a surprise.

The daily event limit field on the webhook form, set to a cap of 3, with a warning that the search matches about 7 records a day and roughly 4 a day will not be sent

In the API

Send max_events_per_day when you create a webhook on POST /v0/webhooks, or change it later on PATCH /v0/webhooks/{id}. Leave it empty for no limit.

To size it, GET /v0/webhooks/volume-estimate returns what the search matches per day and what that costs, so you can pick a cap before you turn the webhook on:

{
    "search_id": 123,
    "records_last_7_days": 1050,
    "avg_records_per_day": 150,
    "credits_per_record": 1,
    "avg_credits_per_day": 150
}

With MCP

The webhook tools are on our MCP server now, so an assistant connected to your workspace can create a webhook, or add a limit to one you already have, without leaving the chat. Tell it to keep a webhook under 100 events a day and it makes the change for you.

More on how the daily event limit behaves, including why records over the cap are dropped rather than queued and what happens when you lower it mid-day, in the daily event limit documentation.

August 11, 2026

Filter jobs by workplace type

You can now filter jobs by workplace type: On-site, Hybrid, or Remote.

The new filter uses the structured workplace_types data already included with every job, so you no longer need to treat remote work as a separate yes-or-no condition. You can also select multiple values when a search should include more than one arrangement.

In the app

Open Job Search, click + Add Filter, and select Workplace type. Choose one or more of On-site, Hybrid, and Remote. The same workplace type filter is available for the jobs inside Company Search.

In the API

Use workplace_types_or in the Jobs API. The request returns jobs containing any of the selected workplace types:

{
  "workplace_types_or": ["hybrid", "remote"],
  "posted_at_max_age_days": 30,
  "limit": 10
}

The accepted values are on_site, hybrid, and remote. on_site returns jobs where both hybrid and remote are false. In the Company Search API, pass the same field inside job_filters.

The previous remote boolean is now deprecated. Existing requests and saved searches that use it continue to work, but new integrations should use workplace_types_or: replace "remote": true with "workplace_types_or": ["remote"]. Note that "remote": false meant “does not contain remote,” which is not the same as specifically selecting on-site jobs; use "workplace_types_or": ["on_site"] when that is what you need.

August 4, 2026

TheirStack is now keyboard and screen-reader accessible

If you use TheirStack with a keyboard, a screen reader, or a browser zoomed well past 100%, parts of the app used to be awkward and a few were simply closed to you. We went through it criterion by criterion against WCAG 2.2 Level A and AA and fixed what we found — and we have published exactly where we stand on each criterion.

You can now navigate the whole app with a keyboard. That was the biggest gap, and it's the change most people will feel first.

The rest of what you're likely to notice:

  • Tab reaches everything. Controls that were clickable divs — and so invisible to Tab and to the Enter key — are real buttons. Every page starts with a "Skip to main content" link, so you no longer tab through the whole sidebar to get to the table.
  • Focus is visible. Every focusable control draws a focus ring, and dialogs and sticky headers no longer sit on top of the control you just moved to.
  • Text is darker. Secondary text, placeholders, error messages, and invoice and request statuses were between 2.2:1 and 3.9:1 against white. They now clear the 4.5:1 that Level AA asks for.
  • Icon-only buttons say what they do. Roughly forty of them announced as "button" and nothing else. They have names now.
  • The page has a shape. Headings form a real outline with no skipped levels, and landmarks are labelled, so jumping by heading or by region actually works.
  • It reflows. Every page fits in a 320px-wide viewport without horizontal scrolling, and survives the line and letter spacing that reader extensions apply.

One find worth calling out: the page title was rendered inside a container hidden below the md breakpoint. On a phone, no page in the app had a heading at all — a screen reader had nothing to announce the page by. That is fixed everywhere at once.

Where we stand

Every page of the app now passes the Level A and AA checks a machine can decide, and passes them again on every change we ship, so this doesn't quietly regress.

What's left is the part no tool decides: whether an alt text describes the right thing, whether the reading order makes sense non-visually, whether a screen reader announces a change in a way you can follow. That's a pass with a real screen reader, and it's the next thing on this list rather than something we're going to claim early.

So instead of a badge, we publish the detail: our Accessibility Statement lists every success criterion we verify automatically, every one we've reviewed by hand, and the ones we haven't assessed yet. If you're working through a procurement questionnaire, that's the page you want — and if it doesn't cover the criterion you need, ask us rather than infer it.

August 3, 2026

LinkedIn company IDs now work in company and job search

LinkedIn identifies a company page two ways: by its vanity slug (linkedin.com/company/deloitte) and by its numeric company ID (linkedin.com/company/1038). Both open the same page, and which one you end up storing depends on where you collected it.

Until now company_linkedin_url_or only ever compared the URL against our stored slug. A URL built on the numeric ID was compared against slugs like deloitte, so it could never match — and it failed quietly: the request succeeded, returned zero results, and consumed no credits. It looked like we simply did not have the company.

That is fixed, in the same filter. All of these now find Deloitte, in both Company Search and Job Search:

{ "company_linkedin_url_or": ["https://www.linkedin.com/company/1038"] }
{ "company_linkedin_url_or": ["https://www.linkedin.com/company/deloitte"] }
{ "company_linkedin_url_or": ["1038"] }
{ "company_linkedin_url_or": ["deloitte"] }

If your calls already send numeric-ID URLs, they start matching on their own — there is nothing to change on your side, and no new filter to migrate to. A value that is entirely digits is matched against both the company's LinkedIn ID and its slug, so you never have to work out which of the two you are holding. Some companies genuinely have a numeric vanity slug, and this way both resolve.

In the app the same filter covers all of it: Linkedin URL, under Company in Company Search and Job Search, accepts a full URL, a bare slug or a numeric ID, one per line.

A note on coverage

The numeric LinkedIn ID is our least complete company identifier: we have one for about 14% of companies, against 26% with a LinkedIn slug and 29% with a domain. This change removes a failure that was total and silent, but it does not make every LinkedIn lookup resolve — an identifier we do not store still returns no results.

If you have the company's domain, company_domain_or remains the identifier that matches most often.

July 27, 2026

Company identifiers in the Technographics dataset

The Technographics dataset now includes the company identifiers needed to use it on its own:

  • company_name
  • domain
  • linkedin_url

Previously, identifying a company by its domain or LinkedIn URL required joining the Technographics dataset with the separate Companies dataset. That meant ingesting and joining two large files even when you only needed technology adoption signals. These identifiers now come directly from the same canonical company record used across TheirStack.

The new columns are available in Technographics v2.1 across CSV and Parquet deliveries, including downloadable sample files. Version 2.1 is a backward-compatible additive update: existing columns and their meanings are unchanged. See the Technographics data dictionary for the complete schema.

July 20, 2026

Searches are up to 9x faster at higher limits

Searches are up to 9x faster at higher limits

Until now, response times could climb sharply when you raised the result limit and asked for more companies or jobs in each query. We have optimized our backend so that requesting more results has a much smaller impact, and response times now stay much closer to constant as limits grow.

The improvement applies to searches in the app and through the API, and is especially visible at higher limits. Searches were about 3x faster overall, and the slowest company searches are now up to 9x faster.

Average response times by requested result limit:

  • Limit 25: 4.08s before -> 1.99s after, about 2.0x faster.
  • Limit 100: 7.76s before -> 2.62s after, about 3.0x faster.
  • Limit 200: 12.27s before -> 3.25s after, about 3.8x faster.
  • Limit 500: 34.68s before -> 5.25s after, about 6.6x faster across the company-search queries measured at this limit.

How we measured it

The figures shown summarize 160 end-to-end requests: 64 company-search requests and 96 job-search requests. We tested four company-search types across limits 25, 100, 200, and 500, plus eight job-search types across limits 25, 100, and 200.

For every search type and limit, we sent four requests: two to our production deployment and two to a preview deployment running the optimized code. The order alternated between preview -> production -> production -> preview and production -> preview -> preview -> production, so both deployments appeared at the beginning and end of a run. This reduces the influence of hot-versus-cold queries and other order effects. Each before value is the average of the two production requests, and each after value is the average of the two preview requests.

Both deployments connected to the same production database, so they used the same underlying data and normal live workload rather than isolated copies.

Each measurement covers the complete HTTP request and response, not only the database query. Exact response times can still be higher or lower at different moments as database load changes, so the figures are benchmark averages rather than latency guarantees.

Why higher limits benefit more

Finding the matching rows in our database is only part of the request. The API still has to fetch, join, and build the response object for every company or job it returns.

The optimized search path reduces the work needed to fetch and prepare each response. That matters most when the response contains hundreds of rows: the fixed request overhead stays similar, but the per-row savings compound.

Company searches

For company searches, the after run was faster or equal in all 16/16 measured rows. The largest gains appear at the higher limits, where the before numbers scaled up sharply and the after numbers stayed much flatter.

The benchmark covers common company search shapes: country filters, technology filters, job filters, and combined technology + job filters, measured at limits 25, 100, 200, and 500.

QueryLimit 25Limit 100Limit 200Limit 500
US companies✅ 4.7s -> 1.97s (2.39x)✅ 5.8s -> 1.84s (3.15x)✅ 7.6s -> 1.94s (3.92x)✅ 25.6s -> 2.62s (9.77x)
US companies using Python✅ 4.9s -> 2.45s (2.00x)✅ 8.8s -> 2.98s (2.95x)✅ 14.7s -> 4.00s (3.67x)✅ 44.1s -> 6.57s (6.71x)
US companies hiring software engineers, 30d✅ 5.3s -> 2.13s (2.49x)✅ 9.2s -> 3.66s (2.51x)✅ 14.2s -> 3.87s (3.67x)✅ 34.6s -> 6.18s (5.60x)
US companies using Python and hiring software engineers✅ 6.4s -> 2.56s (2.50x)✅ 10.6s -> 3.35s (3.16x)✅ 17.9s -> 3.92s (4.57x)✅ 34.4s -> 5.61s (6.13x)

Each cell shows before -> after and the speedup for that limit. means the after run was faster or equal. Open a query to try the same measured filter shape in the app.

Company search benchmark grouped by limit

Job searches

Job searches were faster or equal in all 24/24 measured rows across limits 25, 100, and 200.

We measured title filters, keyword filters, description filters, recent-posting filters, and the same query shapes with 30-day posting windows.

QueryLimit 25Limit 100Limit 200
Title: software engineer, 365d✅ 2.8s -> 1.80s (1.56x)✅ 5.7s -> 2.02s (2.82x)✅ 8.5s -> 2.81s (3.02x)
Keyword slug: Python, 365d✅ 3.9s -> 2.20s (1.77x)✅ 6.7s -> 3.09s (2.17x)✅ 10.5s -> 4.14s (2.54x)
Description: Python, 365d✅ 4.7s -> 2.40s (1.96x)✅ 7.8s -> 3.50s (2.23x)✅ 17.4s -> 3.94s (4.42x)
Posted in last 30d✅ 3.6s -> 1.61s (2.24x)✅ 7.1s -> 1.78s (3.99x)✅ 10.0s -> 2.31s (4.33x)
Posted in last 365d✅ 2.9s -> 1.52s (1.91x)✅ 5.6s -> 1.93s (2.90x)✅ 7.9s -> 2.14s (3.69x)
Title: software engineer, 30d✅ 2.8s -> 1.57s (1.78x)✅ 5.5s -> 1.96s (2.81x)✅ 9.8s -> 2.49s (3.94x)
Keyword slug: Python, 30d✅ 3.3s -> 1.72s (1.92x)✅ 8.8s -> 2.55s (3.45x)✅ 11.4s -> 3.81s (2.99x)
Description: Python, 30d✅ 3.6s -> 2.00s (1.80x)✅ 11.5s -> 2.80s (4.11x)✅ 17.3s -> 3.64s (4.75x)

Each cell shows before -> after and the speedup for that limit. means the after run was faster or equal. Open a query to try the same filter shape in the app.

Job search benchmark grouped by limit

What you need to do

Nothing. The improvements apply automatically in the app and through the API, with no workflow or integration changes required.

July 9, 2026

Internship seniority is clearer and more accurate

We made two seniority improvements:

  1. Internship and co-op roles are classified more accurately as junior, especially when the title also contains senior-sounding words.
  2. Job detail pages now show seniority as its own chip, separate from employment status chips such as Internship.

Previously, titles like Senior Marketing Intern, Product Management Intern, Member of Technical Staff Intern, or Intern - Office of the CEO could be classified as senior, staff, or C-level because those signals were checked before the internship signal. Those now correctly resolve to junior.

We also expanded matching for common internship variants such as internship, externship, co-op, co op, and titles with separators like Intern_Engineering or Co-op_Fall 2026.

Here are a few examples from the labeled set we used to validate the change:

This table refers to the seniority value shown in the new seniority chip, used by filters, exports, the API, webhooks, and the MCP server.

JobSeniority beforeSeniority now
Machine Learning Intern✅ junior✅ junior
Human Resources Intern✅ junior✅ junior
Intern, Student Co-Op (RT)✅ junior✅ junior
Product Management Intern - Tools & Processes❌ senior✅ junior
Intern - Office of the CEO❌ c_level✅ junior
Member of Technical Staff Intern❌ staff✅ junior
Mechanical Engineering Co-op_Fall 2026❌ mid_level✅ junior
Intern_Engineering❌ mid_level✅ junior

The classification improvement applies anywhere job seniority is used: job detail pages, Job Search, exports, the API, webhooks, and the MCP server.

June 25, 2026

New: Agent Skills for the TheirStack MCP

We just shipped the first set of Agent Skills for the TheirStack MCP. A skill is a ready-made playbook you drop into Claude Code (or any MCP client): you give it a product website or a handful of customers, and it does the GTM research for you — grounded in real TheirStack data, not guesses. Every result comes back as a live company count that links straight into a pre-filtered search.

Three skills are available today:

  • Find the sales signals for any product or service — Give it a product or service URL. It reads what the offering does and the problems it solves, then maps them to the TheirStack signals a GTM team should run for outbound (technographics, hiring, buying intent), grouped by the offering each one pitches.
  • Find look-alike companies from your won customers — Give it 4–5 closed-won customers. It profiles each one on TheirStack — what they run, hire for, and mention — extracts the customer fingerprint they share, and finds new companies that look just like them, saved as ready-to-use searches.
  • Keyword research for job-description search — Give it a product or service URL. It derives the pains a buyer would describe in a job post, then iterates against real job postings — reading actual descriptions to learn the exact words companies use — and hands back keyword groups, each tied to a feature with a live job count.

All three run on the TheirStack MCP. Sizing every search is free; you only spend credits if you pull the actual companies. Copy any of them into your own setup and point it at a product URL.