API Integrations for MarTech
REST APIs, in the terms a marketer actually needs
Almost every MarTech tool exposes a REST API — a defined set of HTTP endpoints that let another system read or write its data programmatically instead of through the UI. The core vocabulary: a request goes to a specific endpoint (a URL representing a resource, e.g. /v3/contacts), using an HTTP method that signals the intent — GET to read, POST to create, PUT/PATCH to update, DELETE to remove. The tool responds with a status code (200 for success, 401 for bad auth, 429 for rate-limited, 500 for the vendor's own server error) and a body, almost always JSON, containing the requested data or confirming the write.
Understanding this is what separates 'someone else configures every integration' from being able to read API documentation and diagnose why a Zap or a custom sync is failing — most integration failures show up as a specific status code and error message that directly names the problem, if you know how to read it.
POST https://api.hubspot.com/crm/v3/objects/contacts
Headers:
Authorization: Bearer <access_token>
Content-Type: application/json
Body:
{
"properties": {
"email": "jane@company.com",
"firstname": "Jane",
"lifecyclestage": "lead"
}
}
Response: 201 Created
{
"id": "98765",
"properties": { "email": "jane@company.com", ... },
"createdAt": "2026-09-22T10:00:00Z"
}Authentication patterns
Two patterns dominate MarTech API auth. An API key is a single static secret string sent with every request (often as a header or query param) that identifies your account — simple to set up, but it's a long-lived credential that grants whatever access it was issued with, so it must be treated like a password and rotated if ever exposed. OAuth 2.0 is the more common pattern for tools that act on a specific user's behalf (a CRM, an ad platform) — instead of a static key, the integrating app takes the user through an authorization screen, receives a short-lived access token plus a refresh token, and periodically exchanges the refresh token for a new access token without requiring the user to re-authenticate. OAuth is more setup work but scopes access more precisely (an app can request read-only contact access, for example, rather than blanket account access) and lets a user revoke access without changing a shared secret.
API key vs OAuth 2.0
| Aspect | API key | OAuth 2.0 |
|---|---|---|
| Setup complexity | Low — copy a key from account settings | Higher — authorization flow, token exchange, refresh logic |
| Credential lifespan | Long-lived, often permanent until rotated | Short-lived access token + refresh token |
| Scoping | Usually all-or-nothing account access | Granular, requestable scopes (read-only, specific objects) |
| Revocation | Must rotate the key everywhere it's used | User can revoke app access without breaking other integrations |
| Typical use | Server-to-server, internal tools, simple integrations | User-authorized apps, ad platforms, CRMs, most modern SaaS APIs |
Webhooks vs polling
There are two ways to find out something changed in another system. Polling means your system periodically asks the API 'anything new?' (e.g. checking every 5 minutes for new CRM contacts) — simple to implement, but wastes requests when nothing changed and introduces latency up to the polling interval. Webhooks flip the direction: the source system pushes an HTTP POST to a URL you provide the instant an event happens, with no request needed from your side. This is why webhook-based integrations feel 'instant' (a form submission appearing in a CRM within a second) while polling-based ones feel laggy.
Webhooks require your system to expose a publicly reachable endpoint that can receive and validate incoming POSTs — including verifying the request is genuinely from the claimed source (typically via an HMAC signature in a header, checked against a shared secret) rather than a spoofed request, since a webhook endpoint is by definition public-facing.
import hmac, hashlib
def verify_webhook(payload_body: bytes, signature_header: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), payload_body, hashlib.sha256
).hexdigest()
# Constant-time comparison prevents timing attacks
return hmac.compare_digest(expected, signature_header)
# In your webhook handler:
if not verify_webhook(request.body, request.headers["X-Signature"], WEBHOOK_SECRET):
return Response(status=401) # reject unverified requestsAn unverified webhook endpoint is an open door
Rate limits and how MarTech tools actually talk to each other
Every serious API enforces rate limits — a cap on requests per time window (e.g. 100 requests per 10 seconds), returned as a 429 Too Many Requests status when exceeded, often with a Retry-After header telling you how long to back off. Integration tools handle this by queuing and throttling requests rather than firing them all at once; a naive custom integration that doesn't respect rate limits will get throttled or temporarily blocked, which is why bulk operations (syncing 50,000 contacts) use dedicated batch endpoints (submitting many records in one request) rather than one API call per record wherever the vendor offers them.
Understood end to end, most MarTech 'integrations' — whether a native app connector, a Zapier Zap, or a custom reverse-ETL sync — are the same small set of primitives repeated: authenticate (API key or OAuth), read or write via REST endpoints respecting rate limits, and stay current either by polling or by subscribing to webhooks. Every vendor dashboard toggle that says 'connect X to Y' is doing exactly this underneath, just with the plumbing hidden.
What's next
Zapier is the most common no-code tool built entirely on these primitives — seeing how triggers, actions, and webhooks map onto a real no-code automation tool makes the abstract API concepts concrete.
Next: Zapier Automation Patterns →
I build these systems professionally.
Whether it's a RAG pipeline, analytics migration, or AI workflow — let's talk.