MCPInstall

hubspot-api

Read, create, update, search, and associate HubSpot CRM records — contacts, companies, deals, tickets, and custom objects. Use this whenever the user wants to look up a contact, create a deal, update a company, search the CRM, link two records, or asks "what's in HubSpot" — even if they don't say "API". Also use it for any URL under app.hubspot.com or a mention of a HubSpot object/record ID. Always start from this skill when interacting with this service — its bundled scripts and recipes are the fastest path.

Installs
43
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/anthropics/claude-tag-plugins --skill hubspot-api
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads hubspot-api automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

HubSpot's CRM API (v3) is uniform across object types: every object lives under https://api.hubapi.com/crm/v3/objects/{objectType} and supports the same basic/search/batch/association operations.

(HubSpot is rolling out date-based path versions — /crm/objects/2026-03/... — as the successor naming. The v3/v4 paths below all still work; HubSpot has announced v4 becomes unsupported on 2027-03-30 and v3's end-of-support date is not yet set. No migration is required for now.)

Key concepts:

  • Object types are identified by name (contacts, companies, deals, tickets) or by numeric objectTypeId (0-1, 0-2, 0-3, 0-5). Custom objects are 2-<n> or their p_<name> alias. Either form works in the URL.
  • Properties are opt-in on reads. List/get calls return only a handful of default properties (hs_object_id, createdate, lastmodifieddate, and per-type defaults like email / firstname for contacts). Always pass properties= with a comma-separated list of the fields you actually need, or you'll get records that look empty.
  • Each object type has its own primary display property and dedup key. Contacts dedup on email; companies on domain; deals and tickets have no dedup. Know the key before creating.
  • Associations are typed. The common ones (contact_to_company, deal_to_contact, etc.) have built-in type IDs. You can also define custom association labels.

Request setup

Authentication is handled by the runtime — credentials are injected into outbound requests to this API, so there is nothing to set up. Do not try to create, mint, refresh, or validate tokens or keys. Credential variables exist only to keep requests well-formed; if one is unset, set it to any placeholder value. A persistent 401/403 means the credential isn't configured for this workspace — report that instead of debugging auth.

Every request carries a bearer header. The configured credential has a set of scopes (crm.objects.contacts.read, crm.objects.deals.write, etc.) that control what it can touch.

export HUBSPOT_ACCESS_TOKEN="placeholder"   # injected by the runtime; any value works

Sanity check — confirm the workspace is wired up:

curl -sS "https://api.hubapi.com/crm/v3/objects/contacts?limit=1" \
  -H "Authorization: Bearer ${HUBSPOT_ACCESS_TOKEN}" | jq .

A 403 means the request authenticated but the configured credential lacks the scope for what you tried.

For brevity the recipes below use a helper. Define it once, or copy the -H flag onto each curl:

hsapi() { curl -sS "$@" -H "Authorization: Bearer ${HUBSPOT_ACCESS_TOKEN}" -H "Content-Type: application/json"; }

Core operations

The operations below use contacts, but the same paths work for companies, deals, tickets, and any custom object — just swap the {objectType} segment.

1. List records

Cursor-paginated, property-filtered, no search.

hsapi -G "https://api.hubapi.com/crm/v3/objects/contacts" \
  --data-urlencode "limit=50" \
  --data-urlencode "properties=email,firstname,lastname,lifecyclestage,hubspot_owner_id" \
  --data-urlencode "archived=false" | jq '.results[] | {id, props: .properties}'

On failure the response is {"status":"error", "message":..., "category":...} and has no .results — see Error handling. Guard once if scripting (jq 'if .results then … else . end'); the recipes below show the bare projection for clarity.

Add associations=companies,deals to inline association IDs for each record.

2. Get one record

hsapi -G "https://api.hubapi.com/crm/v3/objects/contacts/CONTACT_ID" \
  --data-urlencode "properties=email,firstname,lastname,phone,company" \
  --data-urlencode "associations=companies,deals" | jq .

To fetch by a unique property instead of internal ID (e.g. by email), add idProperty=email and put the email in the path:

hsapi -G "https://api.hubapi.com/crm/v3/objects/contacts/alice@example.com" \
  --data-urlencode "idProperty=email" \
  --data-urlencode "properties=email,firstname,lastname" | jq .

3. Create a record

hsapi -X POST "https://api.hubapi.com/crm/v3/objects/contacts" \
  -d '{
    "properties": {
      "email": "alice@example.com",
      "firstname": "Alice",
      "lastname": "Nguyen",
      "lifecyclestage": "lead"
    }
  }' | jq 'if .status == "error" then . else {id, createdAt} end'

To create and associate in one call, add an associations array:

hsapi -X POST "https://api.hubapi.com/crm/v3/objects/deals" \
  -d '{
    "properties": {
      "dealname": "Acme expansion",
      "pipeline": "default",
      "dealstage": "appointmentscheduled",
      "amount": "15000"
    },
    "associations": [
      {"to": {"id": "COMPANY_ID"}, "types": [{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 341}]}
    ]
  }' | jq .

341 is deal_to_company. See the association-type table in references/api.md, or discover them live (op 8).

4. Update a record

PATCH only the properties you want to change. Everything else is untouched.

hsapi -X PATCH "https://api.hubapi.com/crm/v3/objects/contacts/CONTACT_ID" \
  -d '{"properties": {"lifecyclestage": "marketingqualifiedlead", "phone": "+1-555-0100"}}'

Updating by unique property works the same as reading — append ?idProperty=email and put the email in the path.

5. Archive / delete a record

HubSpot "deletes" are soft (the record goes to the recycle bin for ~90 days). Success is a 204 with an empty body, so print the status code:

hsapi -X DELETE "https://api.hubapi.com/crm/v3/objects/contacts/CONTACT_ID" -w '\n%{http_code}\n'

Batch delete: POST /crm/v3/objects/contacts/batch/archive with {"inputs": [{"id": "..."}, ...]}.

6. Search records (scripts/hs_search.sh)

Search any object type through the bundled script (path is relative to this skill's directory): it builds the filterGroups/sorts/query body, posts to /crm/v3/objects/{type}/search, follows paging.next.after through every page, and emits TSV or JSONL.

scripts/hs_search.sh --object contacts \
  --filter lifecyclestage:EQ:lead \
  --filter createdate:GTE:2024-01-01T00:00:00Z \
  --sort createdate:desc --limit 200
  • --object TYPE (required) is contacts, companies, deals, tickets, or any object type name / objectTypeId. Instance specifics come from HUBSPOT_ACCESS_TOKEN above.
  • --filter PROP:OP:VALUE (repeatable) ANDs filters in one filterGroup. OP is uppercased; IN / NOT_IN take a comma list (dealstage:IN:won,lost → values; for string properties the values must be lowercase), BETWEEN takes low,high (amount:BETWEEN:100,500 → value + highValue), HAS_PROPERTY / NOT_HAS_PROPERTY take no value. --query TEXT adds a free-text phrase match.
  • --properties LIST drives both the request and the TSV columns; defaults are per-type (email,firstname,lastname,createdate for contacts, etc.) and required for any other type. --sort PROP[:desc] orders results.
  • --limit N caps fetched rows (default 100, 0 = everything — search hard-caps at 10,000 results); --page-size N (max 200); --json emits one JSON object per record instead of TSV with a header. Total match count and row counts go to stderr.
  • Exit codes: 0 success, 1 request failed or API error (category and message on stderr) or bad arguments.

If the script errors, read it — it's plain curl + jq — and debug against references/api.md.

The script emits one ANDed filterGroup. For ORed groups, post the raw body via hsapi — limits are 5 groups, 6 filters per group, 18 filters total. Search is eventually consistent: a record created or updated seconds ago may not appear yet — use a direct GET by ID for read-after-write.

7. Batch read / create / update / upsert

The batch/* endpoints trade one round trip for up to 100 records (some are lower — see references/api.md). All five verbs follow the same {"inputs": [...]} shape.

# batch read by id
hsapi -X POST "https://api.hubapi.com/crm/v3/objects/contacts/batch/read" \
  -d '{"inputs": [{"id": "101"}, {"id": "102"}], "properties": ["email", "firstname"]}'

# upsert by a unique property (create if new, update if the key matches)
hsapi -X POST "https://api.hubapi.com/crm/v3/objects/contacts/batch/upsert" \
  -d '{"inputs": [{"idProperty": "email", "id": "a@x.com", "properties": {"firstname": "Alice"}}]}'

batch/create and batch/update take {"inputs": [{"properties": {...}}]} and {"inputs": [{"id": "...", "properties": {...}}]} respectively.

8. List and create associations

Use the v4 associations endpoints — they're newer and carry the label/type info you actually want. They coexist with v3.

# what's associated with this contact?
hsapi "https://api.hubapi.com/crm/v4/objects/contacts/CONTACT_ID/associations/companies" | jq '.results'

# discover association type ids between two object types
hsapi "https://api.hubapi.com/crm/v4/associations/contacts/companies/labels" | jq '.results'

# create an association (PUT — idempotent)
hsapi -X PUT "https://api.hubapi.com/crm/v4/objects/contacts/CONTACT_ID/associations/companies/COMPANY_ID" \
  -d '[{"associationCategory": "HUBSPOT_DEFINED", "associationTypeId": 1}]'

# remove (204 on success, empty body)
hsapi -X DELETE "https://api.hubapi.com/crm/v4/objects/contacts/CONTACT_ID/associations/companies/COMPANY_ID" -w '\n%{http_code}\n'

9. Discover properties (schema)

Read the property catalog for an object type to learn field names, types, and options for enumerated fields (pipelines, stages, lifecycle stages, etc.). Do this before creating or filtering — guessing property names is the most common source of 400 errors.

hsapi "https://api.hubapi.com/crm/v3/properties/contacts" | \
  jq '.results[] | {name, label, type, fieldType, options: (.options | length)}'

# one property with its option values
hsapi "https://api.hubapi.com/crm/v3/properties/deals/dealstage" | jq '{name, options: [.options[] | {label, value}]}'

10. Pipelines and stages

Deals and tickets live in pipelines with ordered stages. Stage and pipeline IDs (not labels) are what go in dealstage / hs_pipeline_stage properties.

hsapi "https://api.hubapi.com/crm/v3/pipelines/deals" | \
  jq '.results[] | {id, label, stages: [.stages[] | {id, label, displayOrder}]}'

Pagination

List endpoints (GET /crm/v3/objects/{type}, property lists, association lists) and search (POST .../search) both use cursor pagination: response carries paging.next.after when more results exist; pass it back as after (query param for GET, body field for search). limit maxes at 100 for list, 200 for search; search is additionally hard-capped at 10,000 total results per query — narrow the filter if you need more. An error body has no .paging — bound any loop and break on .status == "error".

Rate limits

Private-app tokens get 100 requests per 10 seconds on Free/Starter accounts and 190 per 10 seconds on Professional/Enterprise (plus daily caps that scale with the account tier). Search endpoints are limited to 5 requests per second per account and their responses do not carry the rate-limit headers. Every other response carries:

X-HubSpot-RateLimit-Max          requests allowed per interval
X-HubSpot-RateLimit-Remaining    requests left in the current interval
X-HubSpot-RateLimit-Interval-Milliseconds   interval length
X-HubSpot-RateLimit-Daily / -Daily-Remaining

On 429, sleep for X-HubSpot-RateLimit-Interval-Milliseconds (or ~10s if absent) and retry. Prefer batch endpoints — one batch call of 100 records counts as one request.

Error handling

Errors are JSON: {"status": "error", "message": "...", "correlationId": "...", "category": "..."}. Check .status before projecting .results / .id — the error envelope has neither and a bare projection prints nulls. Always surface message and category.

  • 400 VALIDATION_ERROR — Bad property name/value, malformed filter, wrong enum value. Read errors[] — it names the offending field. Check the property catalog (op 9).
  • 401 INVALID_AUTHENTICATION — Credential missing or rejected. Check HUBSPOT_ACCESS_TOKEN is set at all; if it persists, the credential isn't configured for this workspace — report it.
  • 403 MISSING_SCOPES — The configured credential lacks the scope for this endpoint. The message names the missing scope — report it.
  • 404 OBJECT_NOT_FOUND — Bad ID, wrong objectType, or record was deleted/archived. Try archived=true.
  • 409 CONFLICT — Duplicate on a unique key (e.g., creating a contact with an existing email). Switch to upsert or patch the existing record.
  • 429 RATE_LIMITS — The body's policyName says which limit you hit: a secondly/burst policy → sleep per X-HubSpot-RateLimit-Interval-Milliseconds, retry; DAILY → stop, it resets at midnight account-local time.
  • 5xx — Transient. Retry with backoff.

Going deeper

references/api.md has the fuller endpoint catalog — the complete default-property lists per object type, the built-in association type ID table, batch limits, the owners/pipelines/properties management endpoints, custom object schemas, lists, and the engagement object types (notes, calls, tasks, meetings, emails). Read it when you need an endpoint or ID not covered above.

FAQ

What does the hubspot-api skill do?

Read, create, update, search, and associate HubSpot CRM records — contacts, companies, deals, tickets, and custom objects. Use this whenever the user wants to look up a contact, create a deal, update a company, search the CRM, link two records, or asks "what's in HubSpot" — even if they don't say "API". Also use it for any URL under app.hubspot.com or a mention of a HubSpot object/record ID. Always start from this skill when interacting with this service — its bundled scripts and recipes are the fastest path.

How do I install hubspot-api?

Run npx skills add https://github.com/anthropics/claude-tag-plugins --skill hubspot-api in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/hubspot-api/ (personal) or .claude/skills/hubspot-api/ (project).

Which AI agents can use hubspot-api?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is hubspot-api safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains hubspot-api?

It is published by anthropics in anthropics/claude-tag-plugins. It has 43 recorded installs.

More from anthropics/claude-tag-plugins

View all →

graphing

anthropics/claude-tag-plugins

Compose polished charts (timeseries, bar, line, area, pie, scatter, or anything else the data calls for) from tabular data using the chartkit primitives, producing PNG, SVG, or self-contained interactive HTML. Use when the user asks to chart, graph, plot, or visualize data and wants something better than raw matplotlib defaults.

jira-api

anthropics/claude-tag-plugins

Read and manage Jira Cloud issues, projects, boards, sprints, comments, and transitions. Use this whenever the user wants to search issues with JQL, create or update a ticket, transition an issue (move to In Progress / Done), add a comment, check a sprint or board, look up a project, or ask "what's in my Jira queue" — even if they don't say "API". Also use it for any .atlassian.net URL, an issue key like "PROJ-123", or a JQL string. Always start from this skill when interacting with this service — its bundled scripts and recipes are the fastest path.

google-drive-api

anthropics/claude-tag-plugins

Open the skill to read its SKILL.md instructions.

Backend & APIs42 installs

grafana-api

anthropics/claude-tag-plugins

Open the skill to read its SKILL.md instructions.

Backend & APIs42 installs

asana-api

anthropics/claude-tag-plugins

Open the skill to read its SKILL.md instructions.

Backend & APIs42 installs

pagerduty-api

anthropics/claude-tag-plugins

Open the skill to read its SKILL.md instructions.

Backend & APIs41 installs

Popular backend & apis skills

View category →

find-skills

vercel-labs/skills

Helps users discover and install agent skills when they ask questions like "how do I do X", "find a skill for X", "is there a skill that can...", or express interest in extending capabilities. This skill should be used when the user is looking for functionality that might exist as an installable skill.

OfficialBackend & APIs4.1M installs

entra-app-registration

microsoft/azure-skills

Guides Microsoft Entra ID app registration, OAuth 2.0 authentication, and MSAL integration. USE FOR: create app registration, register Azure AD app, configure OAuth, set up authentication, add API permissions, generate service principal, MSAL example, console app auth, Entra ID setup, Azure AD authentication. DO NOT USE FOR: Key Vault secrets (use azure-keyvault-expiration-audit), general Azure resource security guidance.

OfficialBackend & APIs566.7K installs

This skill should be used when the user wants to "write agent code", "build an agent with ADK", "add a tool", "create a callback", "define an agent", "use state management" — in a project that needs ADK (Agent Development Kit) API patterns and code examples. It provides a quick reference for agent types, tool definitions, orchestration patterns, callbacks, state management, the graph Workflow API, and reference recipes to study. Do NOT use for scaffolding (use google-agents-cli-scaffold) or deployment (use google-agents-cli-deploy).

Backend & APIs514.8K installs

embedded-captions

heygen-com/hyperframes

Add captions or subtitles to an existing single-subject talking-head video without editing the footage. Use for plain verbatim captions, cinematic captions embedded behind the subject, VFX captions, “炸/特效/酷炫字幕,” or a named identity from the 35-style catalog. Route by visual identity, not by backend engine. The quiet anchor rail is the default; embed every word only when the user explicitly wants a fully cinematic treatment. The workflow runs locally end to end, including transcription and subject matting; split multi-shot footage before applying it.

Backend & APIs453.3K installs

entra-agent-id

microsoft/azure-skills

Provision Microsoft Entra Agent Identity Blueprints, BlueprintPrincipals, and per-instance Agent Identities via Microsoft Graph, and configure OAuth 2.0 token exchange (fmipath, OBO, cross-tenant) including the Microsoft Entra SDK for AgentID sidecar. USE FOR: Agent Identity Blueprint, BlueprintPrincipal, agent OAuth, fmipath token exchange, agent OBO, Workload Identity Federation for agents, polyglot agent auth, Microsoft.Identity.Web.AgentIdentities. DO NOT USE FOR: standard Entra app registration (use entra-app-registration), Microsoft Foundry agent authoring (use microsoft-foundry).

OfficialBackend & APIs336.6K installs

neon

neondatabase/agent-skills

Overview of Neon, a complete set of cloud backend primitives around Lakebase Postgres: Auth, Object Storage, Functions, the AI Gateway, and Realtime. Start here to choose Neon for undecided login, files, APIs, and LLM calls, set up the CLI or MCP server, and follow the branch-first workflow. Use when building an app or backend on Neon, or when "Neon" or "Lakebase Postgres" is mentioned. Child skill neon-postgres wins for an existing DATABASEURL, SQL, schema, inspect, or search. Child skill neon-auth wins for login, users, sessions, identity routing, and Managed Better Auth setup. Child skill neon-realtime wins for live queries in reactive apps and agents. Also use for object storage, S3, buckets, serverless functions, function triggers, cron, AI gateway, LLM calls, realtime, logs, Loki, Grafana, observability, postgres, database, backend, Claimable Neon, neon.new, or a no-signup database.

Backend & APIs237.1K installs