Detecting the AI Stack

How do you detect which AI models a company uses?

You detect which AI models a company uses by fingerprinting the provider SDKs, package names, and API hostnames its front end loads and calls. Anthropic, OpenAI, Gemini, Cohere, and Mistral each leave a recognizable trail, but the exact model version usually lives in a server-side request body and stays invisible, so you infer the provider more reliably than the specific model. Bassethound reads these signals in the AI-readiness layer of its five-layer dossier in a single keyless call.

Best move: read the SDKs and API hostnames the front end loads, and treat “provider” as the answer you can trust more than “model version.”

Why it works: an app that calls a model has to load a client and hit an endpoint, and those signatures (@anthropic-ai/sdk, api.openai.com, a Vercel AI SDK bundle) survive minification. The exact model string does not, because it lives in a server-side request body.

Key takeaways

  • Model detection is provider detection first: you fingerprint Anthropic, OpenAI, Gemini, Cohere, or Mistral by their SDKs and API hostnames, not by scanning the homepage for the word “AI.”
  • The specific model version usually stays server-side, so naming the provider is honest and naming the exact model is often a guess.
  • Provider-agnostic layers like the Vercel AI SDK confirm an AI pattern but not the vendor behind it.
  • A fully proxied or purely server-side call leaves no client-visible signal, and absence of a signal is not proof there is no model.
  • Bassethound returns the model provider alongside the SDK, orchestration framework, and vector store in one correlated dossier, and flags what it could not see.

What leaks a model provider from the outside?

Three signatures do most of the work. First, provider SDK package names in the bundled JavaScript. An @anthropic-ai/sdk import points at Claude, an openai client points at GPT, and a Vercel AI SDK bundle points at a provider-agnostic wrapper sitting over one of them. Second, API hostnames in the network calls. Direct browser calls to api.anthropic.com or api.openai.com are unambiguous. Third, embedded AI widgets. An Intercom Fin or Sierra agent on the page tells you a company shipped a customer-facing LLM feature even when it hides the underlying model.

Any one of these is a lead. Two together is a confident provider match. What you will not get from these signatures alone is the tier or version, because the request that names the model runs on the company’s servers, not in your browser.

The reliable read is layered: name the provider from the SDK or hostname, note the SDK pattern (direct vendor client versus agnostic wrapper), and record the widget separately as a product-level signal. Bassethound collects all three and correlates them, so a Vercel AI SDK bundle plus an api.openai.com call resolves to “OpenAI, via an agnostic wrapper” instead of two disconnected hits.

Why can’t you always see the exact model version?

Because the model identifier is a field in the request, and the request goes server-side. When an app sends {"model": "..."} to a provider from its backend, your crawl never touches that payload. You see the company domain and, at best, a streaming response coming back. You do not see the string that names the tier.

There are exceptions worth checking. Some client-side apps hold the model name in a config object, a feature flag, or a dropdown of user-selectable models. A chat product that lets users pick between models often ships that list to the browser. When that happens, the version is right there in the client state, and Bassethound reads it.

The honest default is provider-level truth. Claiming to detect an exact model version on every site would mean inventing data that is not observable. Bassethound reports the provider with confidence, reports the version only when the client exposes it, and marks the rest as a known gap. That distinction matters for anyone using the signal to qualify accounts: “runs OpenAI” is a fact you can act on, and “runs a specific GPT tier” is usually a claim you cannot verify from outside.

What does an AI SDK tell you that a hostname doesn’t?

An SDK tells you intent and shape, not just destination. A hostname is a single call to one endpoint. An SDK is a dependency the team chose to build on, which is a stronger tell about how they work.

The three SDKs Bassethound fingerprints split cleanly. The Anthropic SDK and the OpenAI SDK each pin a provider directly, so their presence answers the “which vendor” question on its own. The Vercel AI SDK is different: it is a provider-agnostic abstraction, so it confirms a company built a real streaming AI surface but leaves the vendor to a second signal (the hostname or a nested client). Reading the SDK also catches multi-provider setups. A codebase that loads both the Anthropic and OpenAI clients is hedging or routing between models, which is a more advanced posture than a single hardcoded call.

SDKs survive minification because their exported symbols and internal call patterns are stable. That makes them durable even when a site strips comments and renames variables. Bassethound treats the SDK as its own signal group, distinct from the raw hostname, so a page that loads a client but proxies the actual call still counts as an AI signal instead of vanishing.

How does Bassethound turn model signals into an AI-readiness verdict?

Model provider is one signal group inside a larger count. Bassethound tracks six kinds of AI evidence across the readiness layer: model providers, AI SDKs, orchestration frameworks, vector stores, LLM observability, and machine-facing endpoints like MCP or llms.txt. It computes an ai_readiness score as the count of distinct groups that fire, out of six, capped at 1.0.

The verdict follows from what fired. A site earns shipping when a machine-facing endpoint is present (an MCP endpoint, a .well-known AI file, or a technical llms.txt) or when at least three signal groups fire. One group firing lands it at experimenting. Nothing at all is none. On top of the verdict, Bassethound emits an icp_signal of hot, warm, or cold for fit scoring.

So a company that loads the Anthropic SDK, calls a Pinecone endpoint, and runs LangChain hits three groups and reads as shipping, even if you never learn the exact Claude version. That is the point: the verdict measures whether a company builds with AI, and provider detection feeds it without needing the model string. One correlated dossier, one keyless call, and the score carries its own gaps so you know what it is built on.

When does the model stay completely invisible?

When there is nothing client-visible to fingerprint. A backend that calls a provider server-side, renders the result as plain HTML, ships no vendor SDK, and exposes no API hostname gives the outside world nothing. Same for a fully proxied setup where every model call routes through the company’s own domain. From the crawl’s view, that traffic is indistinguishable from any other backend request.

This is a real limit, not an edge case. Plenty of mature AI products deliberately keep inference server-side for cost, security, and prompt-secrecy reasons. Detection reads the surface, and a well-hidden surface reads as quiet.

Bassethound handles this the only honest way: it reports the gap. When the static crawl and the optional JS render both come back without a provider signal, the dossier says so rather than scoring a confident none that a competitor’s server logs would contradict. Absence of a signal means “not observed from outside,” not “no AI here.” Anyone using the readiness layer to prioritize outreach should read a blank provider field as an unknown to verify, not a disqualification. Bassethound draws that line for you instead of blurring it into false precision.

Bassethound perspective

Most tools that claim to detect a company’s AI stack will happily name the exact model, and most of the time they are guessing. The model version lives in a server-side request body. You cannot see it from a crawl, so a tool that reports it on every site is inventing data. That is the claim a competitor will dispute, and we will defend it: provider is observable, model version usually is not, and pretending otherwise trades trust for a prettier field.

Bassethound reports what the front end loads and calls. It names the provider with confidence when the SDK or hostname is there, names the version only when the client genuinely exposes it, and marks the rest as a gap. A blank field means “not observed,” and that honesty is worth more than a fabricated model string, because you are qualifying real accounts on it. Read the stack, not the story, and say plainly where the stack goes dark.

Sources

Frequently asked questions

Can you see the exact model version a company runs?

Rarely from the outside. The model string (say, a specific Claude or GPT tier) travels to the provider in a server-side request body. You can usually name the provider from the SDK or API hostname, but the version stays hidden unless the app leaks it in client config.

Does model detection need JavaScript rendering?

Sometimes. Server-rendered SDK imports and hardcoded API hostnames appear in static HTML. A client that only wires up its provider in the browser needs JS rendering to surface. Bassethound renders when the static crawl comes up thin and reports what it could not reach.

What if the app routes model calls through its own backend?

Then the provider can go dark. A fully proxied setup shows you the company's own domain, not the model vendor behind it. Bassethound marks this as a gap rather than guessing a provider it cannot see.

Is detecting an SDK the same as detecting the model?

No. An SDK names the provider and often the family (Anthropic SDK means Claude, OpenAI SDK means GPT). A provider-agnostic layer like the Vercel AI SDK tells you the pattern but not the vendor, so you treat it as a weaker, secondary signal.

Can a company use a model with no detectable signal?

Yes. Purely server-side inference with no client SDK, no vendor hostname, and no AI widget leaves nothing to fingerprint from the outside. Absence of a signal is not proof of absence of AI, and Bassethound says so instead of scoring it as zero.

Sniff a domain.

Run sniff_domain on any site and read its five-layer dossier in one call.

Sniff a domain