Founding keys are opena year free at 6,000 req/hour, for the first 1,000 developersClaim yours

HubSpot

Point a HubSpot client at https://hubspot.sandboxapis.dev — @hubspot/api-client takes basePath on the Client constructor, and every SDK takes a base URL; it replaces https://api.hubapi.com. Paths mirror HubSpot exactly. Any token, or none, is accepted. WHAT IS SERVED: companies, contacts, deals (written /crm/v3/objects/0-3, the spelling HubSpot's own document uses) and tickets, each with its collection, its get-by-id, its POST .../search and its POST .../batch/read, with paging.next.after cursors, ?properties=, ?propertiesWithHistory= and ?associations=; plus the property catalogue and its groups under /crm/v3/properties/, both pipelines and their stages under /crm/v3/pipelines/, the owner roster at /crm/v3/owners, and the custom-object schema list. READ THIS BEFORE YOU START: the CRM data itself arrives in generation hello-14, and every generation after it inherits it. Live rides hello-19. The newest pin, hubspot-v3-g11, rides hello-16; hubspot-v3-g10 rides hello-15 and hubspot-v3-g9 rides hello-14. All four of those carry the CRM. Only hubspot-v3-g8 is older — it rides hello-13 and answers those rows with a 404 whose message says exactly that ("This universe predates the CRM canon..."). It is NOT an empty list, deliberately: an empty collection would tell you this org has no contacts, which is false and which no SDK could detect. Two more 404s to tell apart — a path outside coverage carries x-sandboxapis-coverage-kind: endpoint, and a record that simply does not exist carries no coverage header at all. Values in enumerated properties are this portal's own vocabulary; read /crm/v3/properties/{objectType} to learn it, exactly as you would against a real HubSpot portal. Check https://sandboxapis.dev/providers/hubspot for the row-by-row coverage.

REST · 32 endpointsMCP-ready

Coverage badge

Full32/32 · 100%

The share of read rows with a final answer — served and verified, a deviation, retired upstream, or a reviewed empty/refusal. Computed in the coverage manifest, copied here.

Live host
hubspot.sandboxapis.dev
Pinned hosts
4 — generation 8, 9, 10, 11
Serving since
2026-09-04
Lifecycle
Live

Served & verified

31 / 97%

Answering with real universe data, each response checked against HubSpot's published spec by the conformance suite on this build.

The full read API

32

Every read surface HubSpot publishes, deferred long tail included, minus the rows excluded by policy. Writes are out of scope: this universe is read-only.

Not served yet

1

Each returns an explicit, provider-shaped coverage error naming the gap — never invented data.

01 / The swap

Point your client at a different base URL.

No SDK of ours, no shim, no recorded fixtures. The same client library you already use, one environment variable different.

shell
curl "https://hubspot.sandboxapis.dev/crm/v3/objects/companies?limit=5"

Verified drop-in clients

No client library is version-pinned against HubSpot yet, so this page claims none. What is asserted is the wire: every response above is validated against HubSpot's own published spec by the conformance suite. Client pins live in coverage/client-pins.yaml and are added as each SDK joins the suite.

02 / Coverage by family

Every read surface, grouped the way HubSpot groups it.

All 32 rows the coverage manifest carries for HubSpot, deferred long tail included and nothing capped. Open a family, or filter by path to find the exact endpoint your client calls.

Status — what a conformance test found

served & verified
answers with real universe data, and this build checked that response against the vendor's spec.
deviation
served and faithful to the real provider, but failing the vendored spec — usually a bug in the spec.
retired
the vendor removed the endpoint; snapshots pinned before that date still serve it.
planned / deferred
not served yet — an explicit coverage error naming the gap, never invented data.
excluded
out of the claim by policy (writes, and surfaces we refuse); not in any denominator on this page.

Mode — what kind of answer a row gets

derive
the response is computed from artifact rows that already exist
generate
canon does not carry this yet; the generator will produce it, then derive
empty
the true answer for this universe is an empty collection — reason + reviewed date required
refuse
mirror the provider's OWN refusal (e.g. its 403 for a non-admin token) — reason + reviewed date required
read-only
a write named in the manifest because clients probe it; the read-only 403 IS its final response, and it never joins the badge denominator

A row with no mode shown has not been judged yet. Modes are the manifest's own words, from coverage/MODES.yaml; every empty and refuse carries a written reason and a review date before it counts as final.

Badge — where Full starts

Full
every published read row has a final answer
Deep
60% up to 100%
Partial
25% up to 60%
Preview
under 25%

REST + GraphQL + git rows that are not `excluded`. Write operations are NOT rows (DECISIONS 2026-09-01 decision 9): they are counted in meta.write_operations and never enter this ratio.

32 read surfaces in 14 families

pipelines6 of 6 served & verified
GET
/crm/v3/pipelines/{objectType}

the deal and ticket stage vocabularies. Derived from canon's own DEAL_PIPELINE and TicketStatus sets rather than from artifact rows — a pipeline is org configuration, not simulation output, which is exactly why canon holds it as a static table. Projected into renderer-hubspot/src/pipelines.ts because no renderer in this fleet depends on @sandboxapis/canon, and held to canon stage-for-stage by conformance

served & verified
Next
GET
/crm/v3/pipelines/{objectType}/{pipelineId}
served & verified
Next
GET
/crm/v3/pipelines/{objectType}/{pipelineId}/audit

SERVED. A pipeline audit lists the CHANGES made to a pipeline, and nothing in this simulation ever edits one: canon's DEAL_PIPELINE is a static table precisely so a deal's probability cannot move under a pinned customer, and the ticket statuses are Zendesk's fixed six. Both pipelines are the portal's DEFAULT pipelines — the document's own `createdAt = 0` case — and an unedited default pipeline has an empty audit trail upstream too. This is not difficulty laundered as coverage: there is no audit canon being withheld, and the day the simulation grows pipeline editing the row goes red rather than hiding it

served & verified
Next
GET
/crm/v3/pipelines/{objectType}/{pipelineId}/stages
served & verified
Next
GET
/crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}
served & verified
Next
GET
/crm/v3/pipelines/{objectType}/{pipelineId}/stages/{stageId}/audit

SERVED, for exactly the reason the pipeline audit above is: a stage on an unedited default pipeline has no recorded changes. The two rows are one fact read at two granularities, and both go red together the day pipeline editing enters the simulation

served & verified
Next
companies3 of 3 served & verified
GET
/crm/v3/objects/companies
served & verified
Core
GET
/crm/v3/objects/companies/{companyId}
served & verified
Core
POST
/crm/v3/objects/companies/search

search is a READ HubSpot expresses as a POST, because its criteria are a document rather than a query string. All thirteen Filter.operator members, both filter-group levels, `query`, `sorts` and cursor paging are evaluated against the property map each record renders to — the same map the object read serves, so a filter written against what a caller can see selects what a caller expects

served & verified
Core
contacts3 of 3 served & verified
GET
/crm/v3/objects/contacts
served & verified
Core
GET
/crm/v3/objects/contacts/{contactId}
served & verified
Core
POST
/crm/v3/objects/contacts/search
served & verified
Core
deals3 of 3 served & verified
GET
/crm/v3/objects/0-3

deals, from the hello-14 CRM canon. The path reads `0-3` rather than `deals` because that is how HubSpot's own pinned document writes the object type — upstream's asymmetry, carried rather than corrected, and the `deals` spelling real HubSpot also answers is deliberately NOT served (no vendored document declares it)

served & verified
Core
GET
/crm/v3/objects/0-3/{dealId}
served & verified
Core
POST
/crm/v3/objects/0-3/search
served & verified
Core
object-schemas2 of 3 served & verified
GET
/crm-object-schemas/v3/schemas

SERVED, and the empty SURVIVED the CRM canon — which is the re-review the prep wave owed this one (review §3: 'if hello-14 introduces a custom object type for olympus-labs, this row becomes derive and the empty is wrong'). It did not. This endpoint lists CUSTOM object schemas, and the document says so three times over — the operation summary is 'Retrieve all custom object schemas.', the single read's is 'Retrieve the schema of a specified custom object.', and info.x-hubspot-introduction reads 'Use the custom objects schema API to define new types of CRM records in your account.' hello-14 adds STANDARD objects (companies, contacts, deals, tickets), which this API never lists. So the empty is true, it is true for a reason that does not expire, and conformance.test.ts now asserts BOTH halves: that the CRM is demonstrably non-empty on this artifact and that this list still is

served & verified
Next
POST
/crm-object-schemas/v3/schemas/batch/read

SERVED. The batch reader returns the subset of the requested CUSTOM object schemas that exist, and none of them ever does — the same non-expiring fact behind the collection row above, read with a request body instead of a query. `inputs: []` and `inputs` naming types that were never defined both answer the same empty `results`

served & verified
Next
GET
/crm-object-schemas/v3/schemas/{objectType}

refuse — SERVED as HubSpot's own not-found, which is the truthful answer for every object type a caller can name here. olympus-labs defines no CUSTOM object types, so there is no schema to fetch; and a STANDARD type (companies, contacts, deals, tickets) is refused too, for the same reason the collection above does not list one — this is the custom-object API. The prep wave left this `generate` because 'the shape of that 404 is not settled'; it is settled the way every other refusal on this host is, in HubSpot's own Error envelope, and it carries NO coverage header because the endpoint IS served and the schema is what does not exist

deferred
Next
tickets3 of 3 served & verified
GET
/crm/v3/objects/tickets
served & verified
Core
GET
/crm/v3/objects/tickets/{ticketId}
served & verified
Core
POST
/crm/v3/objects/tickets/search
served & verified
Core
owners2 of 2 served & verified
GET
/crm/v3/owners

the go-to-market roster, and the identity join review §8.2 called the serving wave's second deliverable: every CRM record's hubspot_owner_id resolves to an owner whose email is a canonical person's, and parity/hubspot-crm-parity.test.ts walks that join through the hosts rather than through the artifact

served & verified
Core
GET
/crm/v3/owners/{ownerId}
served & verified
Core
properties2 of 2 served & verified
GET
/crm/v3/properties/{objectType}

the property catalogue. The prep wave left this `generate` because 'the pinned documents declare the SHAPE of a property and never the default LIST, so serving one now would mean publishing a catalogue transcribed from memory'. That objection is answered rather than waived: the catalogue is olympus-labs' OWN, derived from canon's columns, and every enumerated property DECLARES its vocabulary in its own `options` — which is how a real HubSpot portal works, since option sets are per-portal configuration. The one thing that IS transcribed from the document is the DEFAULT property set, which each of the four object documents prints in info.description as a table ('Properties returned by default')

served & verified
Core
GET
/crm/v3/properties/{objectType}/{propertyName}
served & verified
Core
property-groups2 of 2 served & verified
GET
/crm/v3/properties/{objectType}/groups
served & verified
Next
GET
/crm/v3/properties/{objectType}/groups/{groupName}
served & verified
Next
companies-batch1 of 1 served & verified
POST
/crm/v3/objects/companies/batch/read
served & verified
Next
contacts-batch1 of 1 served & verified
POST
/crm/v3/objects/contacts/batch/read
served & verified
Next
deals-batch1 of 1 served & verified
POST
/crm/v3/objects/0-3/batch/read
served & verified
Next
properties-batch1 of 1 served & verified
POST
/crm/v3/properties/{objectType}/batch/read
served & verified
Next
tickets-batch1 of 1 served & verified
POST
/crm/v3/objects/tickets/batch/read
served & verified
Next

03 / What's simulated

One data set, rendered in HubSpot's dialect.

What another provider has to agree with, and the test that makes it

Each line below is one assertion in the conformance suite named beside it — run on every build, over one artifact, through the real renderers. Nothing is claimed here that no expect checks.

05 / Pinned snapshots

Frozen universes, on their own hostnames.

Each pin regenerates byte-identically on every request, so a test written against one never drifts. Generations are DIFFERENT universes, not versions of one — never swap a suffix expecting the same data.

PinHostUniverse generationHubSpot API versionRepository files
hubspot-v3-g10hubspot-v3-g10.snap.sandboxapis.devgeneration 10v3served
hubspot-v3-g11hubspot-v3-g11.snap.sandboxapis.devgeneration 11v3served
hubspot-v3-g8hubspot-v3-g8.snap.sandboxapis.devgeneration 8v3served
hubspot-v3-g9hubspot-v3-g9.snap.sandboxapis.devgeneration 9v3served

Need an endpoint that is not served yet?

Every row HubSpot's manifest carries is on this page, so “not here” is an answer rather than a gap in the rendering. Tell us which path and which client, and it moves up the queue — the order is set by what people ask for.

Request coverage