HubSpot's CRM API treats contacts, companies, deals, and tickets as objects at https://api.hubapi.com/crm/v3/objects/<type>. Fields are called properties, and you ask for the ones you want.
Create a private app under Settings → Integrations → Private Apps with CRM scopes (e.g. crm.objects.contacts.read and .write), then copy its access token. Send it as Authorization: Bearer <token>. In the examples it's written {{HUBSPOT_TOKEN}}.
Full reference: https://developers.hubspot.com/docs/api-reference/overview
Without properties you get only a few defaults. Page with after from paging.next.
curl "https://api.hubapi.com/crm/v3/objects/contacts?limit=10&properties=email,firstname,lastname" \
-H "Authorization: Bearer {{HUBSPOT_TOKEN}}"
Returns the new contact with its id. A duplicate email returns 409.
curl -X POST https://api.hubapi.com/crm/v3/objects/contacts \
-H "Authorization: Bearer {{HUBSPOT_TOKEN}}" \
-H "Content-Type: application/json" \
-d '{"properties": {"email": "[email protected]", "firstname": "Jenny", "lastname": "Rosen"}}'
Filters within a group are ANDed; separate groups are ORed.
curl -X POST https://api.hubapi.com/crm/v3/objects/contacts/search \
-H "Authorization: Bearer {{HUBSPOT_TOKEN}}" \
-H "Content-Type: application/json" \
-d '{
"filterGroups": [{"filters": [{"propertyName": "email", "operator": "EQ", "value": "[email protected]"}]}],
"properties": ["email", "firstname", "lastname"]
}'
Same shape as contacts — only the object type changes.
curl "https://api.hubapi.com/crm/v3/objects/deals?limit=10&properties=dealname,amount,dealstage" \
-H "Authorization: Bearer {{HUBSPOT_TOKEN}}"