Every integration project starts with a sentence that sounds like a conclusion: they have an API. It is usually true, and it is almost never the answer to the question that was asked.

An API is an interface a vendor publishes and maintains. An integration is a standing agreement between two systems that keeps them consistent while people, and the systems themselves, keep changing the data underneath. The first is a document. The second is software you own, and it has to survive expired credentials, rate limits, events that arrive in the wrong order, records that already exist on both sides, and the Tuesday afternoon when somebody renames a field.

This guide takes the term apart. What an API integration actually is, the six things every one of them does, why webhooks are not the data source people assume, what rate limits do to a design, and the specific line where a script that works once becomes a system that keeps working. The examples are grounded in HubSpot's published documentation because that is what we build against, but the shape holds for any two systems.

In this article

1.

2.

3.

4.

5.

6.

7.

8.

9.

10.

11.

12.

What Is an API Integration?

An API integration is a connection between two software systems that uses each system's published API to keep them consistent with each other over time.

The three words in that sentence doing the most work are "over time". A one-off script that pulls a thousand contacts out of one system and pushes them into another is a migration, and it is a genuinely different thing. It runs once, a human watches it, and if it half fails somebody looks at the output and fixes it by hand. Nobody is paged at 2am for a migration. An integration runs unattended and indefinitely, which means every failure mode a human would have caught by eye now has to be handled in code.

This is why "does it have an API?" is such a weak qualifying question, and why the answer so rarely predicts the effort. What predicts the effort is a different set of questions entirely: which objects move, in which direction, how fast, what happens to records that already exist on both sides, and who wins when the two copies disagree. Those are answerable in an hour with the right people in the room. None of them are answered by the documentation.

How API Integration Works, Step by Step

Strip away the vendor names and every API integration does the same six things. The proportions differ wildly, but the list does not.

  1. 1

    1. Authenticate

    Exchange a credential for a token the receiving system will accept, and keep doing it. This is the step people assume is one-time setup and it is not: tokens expire, refresh flows fail quietly, and an integration that has worked for a month can die on a Sunday because nobody handled a refresh.

  2. 2

    2. Read

    Call an endpoint to fetch the records in scope, page through the results, and stop cleanly. No API hands you everything at once, so every read is a loop with a cursor, and every loop has to survive the source data changing underneath it while it runs.

  3. 3

    3. Map

    Translate the source system's fields, types and picklist values into the target's. This is where the actual design work lives, and it is the step that generates every awkward question about what a record means. Our guide to data mapping covers this layer on its own.

  4. 4

    4. Write

    Create or update records in the target, in batches where the API supports them, and record what came back. Writing is the step where partial success exists, which is a state a single script almost never handles and a production integration always does.

  5. 5

    5. Detect change

    Learn that something changed without asking about everything. Either subscribe to events the source system emits, or poll for records modified since your last successful run, and keep a durable record of when that was.

  6. 6

    6. Recover and reconcile

    Retry what failed without duplicating what succeeded, park what cannot be retried somewhere a human will see it, and periodically compare both sides to catch the drift nobody noticed. This step is the entire difference between a script and an integration.

Steps two, three and four are the ones tutorials cover. Steps one, five and six are the ones that decide whether the integration is still working in six months, and they are almost never in the demo.

The Step Everyone Underestimates: Credentials That Expire

Authentication is treated as setup, done once, at the start. In production it is a recurring background job with its own failure modes.

HubSpot is a good worked example because its published behaviour covers all three of the common patterns. OAuth access tokens are short-lived and expire 30 minutes after they are generated. The refresh token that mints new ones does not expire on a timer, but it stops working the moment a user uninstalls the app or somebody revokes it. Private app tokens, the ones most single-portal builds actually use, do not expire at all and can be rotated if they leak.

HubSpot's own guidance is to read the expires_in value returned when you generate a token and refresh against that, rather than hardcoding a refresh interval. That advice exists because the expiry has changed before, and any integration that hardcoded the old number stopped working on the day it changed.

The failure mode here is distinctive and worth recognising. Auth failures are not gradual. The integration does not slow down or produce partial results. It works perfectly and then does nothing at all, and because nothing is being written, nothing looks wrong on either screen. If your only monitoring is "did anything error", an integration whose token was revoked can be silently dead for a week.

Webhooks vs Polling: How One System Learns Something Changed

Change detection is the layer where designs most often go wrong, because the two available mechanisms look interchangeable and are not.

Polling means your system asks on a schedule: give me everything modified since the last time I asked. It is simple, it is entirely under your control, and it costs API calls whether or not anything changed. Its weakness is latency. A five minute poll means a five minute lag, and reducing the lag multiplies the call volume.

Webhooks invert that. The source system posts to a URL you host when an event happens, so you find out in seconds and you make no calls when nothing is happening. Its weakness is that you are now running a public endpoint whose availability, throughput and security are your problem, and you are trusting a delivery mechanism you do not control.

The mistake is treating the webhook payload as the data. It is not the data, it is a notice, and HubSpot's documentation is unusually direct about why.

HubSpot does not guarantee that you will receive these notifications in the order they occurred, and points you at each notification's occurredAt property to determine when the event actually happened.

Read that again with a sync in mind. Two edits to the same contact, half a second apart, can arrive at your endpoint in either order. If you apply payloads as they land, the older value wins whenever the ordering flips, and you have built a system that corrupts data occasionally and unreproducibly. The pattern that survives this is to treat the event purely as a signal that a specific record changed, then call the API to fetch that record's current state and write that. You give up nothing except a call, and you get a system whose correctness does not depend on delivery order.

The rest of HubSpot's published webhook behaviour sets the shape of the endpoint you have to build:

  • Five seconds to respond. Take longer to acknowledge a batch of notifications and HubSpot treats it as a failure. This is the single strongest argument for acknowledging immediately and processing asynchronously, because any synchronous work you do inside the handler is work done against a five second clock.
  • Up to 10 retries over 24 hours. Failed notifications are re-sent up to ten times, spread across the next 24 hours, on connection failures, timeouts and any 4xx or 5xx response. That is a generous safety net, and it means your handler will receive duplicates, so it has to be idempotent by construction.
  • Up to 100 events per request, 10 requests in flight. HubSpot batches events into a single POST and caps concurrency at 10 in-flight requests per installing account. Your endpoint should be sized for bursts of a hundred events at a time rather than for a steady trickle.
  • Signatures with a five minute window. Requests carry a signature header and a request timestamp, and the verification guidance is to reject anything whose timestamp is older than five minutes. That is replay protection, and skipping it turns a public URL into an unauthenticated write path into your CRM.

Most production integrations end up using both mechanisms: webhooks for latency, and a scheduled poll or reconciliation pass underneath to catch whatever the webhooks missed. The poll is not redundancy for its own sake. It is the thing that notices when the webhook subscription itself has been broken for two days.

Rate Limits, and Why the Number That Matters Is the Smallest One

Every API meters access, and the published headline number is rarely the one that constrains a design.

HubSpot's limits for privately distributed apps are 190 requests per 10 seconds on Professional and Enterprise, and 100 per 10 seconds on Free and Starter, with a daily ceiling of 250,000 calls on Free and Starter, 625,000 on Professional and 1,000,000 on Enterprise. Publicly distributed OAuth apps get 110 requests every 10 seconds. Cross any of them and you get a 429 on everything that follows, and every response carries headers reporting your ceiling, your remaining allowance and the length of the interval, which is the information a well behaved client should be pacing itself against rather than backing off blindly after it has already been refused.

Now the number that actually shapes the build. HubSpot's CRM Search API, the endpoint you use to ask which records changed since the last run, runs at 5 requests per second. That is roughly one fortieth of the general burst limit, and it applies to precisely the operation a sync performs most.

Two more search constraints follow from the same documentation, and both bite during backfills. A search query is capped at 10,000 total results, and paging past that returns a 400 rather than more records. The maximum page size is 200 objects, with a default of 10 if you do not set one.

The general lesson holds well beyond HubSpot. Read the limits for the specific endpoints your integration depends on, not the platform's headline figure, and design for the smallest one. In every integration we have built, the binding constraint has been either a narrow endpoint like this one or a licensing gate, and effectively never the headline throughput number.

One Way, Two Way, and What Bidirectional Actually Costs

"Two way sync" is the phrase most likely to hide a month of work in a scoping call.

A one way sync has exactly one source of truth for the data it moves. A writes to B, B never writes back, and if somebody edits the record in B the next run overwrites it. That is not a limitation to apologise for, it is a property worth defending, because it makes every question about correctness answerable by pointing at one system.

A bidirectional sync lets both sides write, and that single change introduces three problems that simply do not exist in one direction.

  • Conflicts. Both systems changed the same field since the last run. Something has to decide, and "most recent timestamp wins" is a decision that quietly makes the system with the more aggressive automation the winner of every argument.
  • Echo loops. Your write into B emits a change event from B, which your integration dutifully syncs back into A, which emits an event of its own. Without an explicit way to recognise your own writes, two systems can spend all day agreeing loudly with each other and consuming your entire daily API allowance doing it.
  • Field level ownership. The real answer is rarely that one whole system wins. It is that A owns the email address, B owns the billing status, and the lifecycle stage is written by whichever system observed the event that moved it. Ownership is a per-field decision, and writing it down is most of the work.

There is a fourth constraint that only shows up once you look at a specific API, and HubSpot's contacts documentation carries a good example: when updating the lifecyclestage property, you can only set the value forward in the stage order. A bidirectional design that assumes any value can be written at any time meets that rule in production, not in the plan.

Point to Point, a Platform, or a Custom Build

There are three shapes an integration can take, and the right one depends far more on how many connections you have than on how technical your team is.

Point to point means each pair of systems gets its own dedicated connection. It is the correct starting shape, and it stays correct for longer than the diagrams in platform vendors' marketing suggest. The genuine argument against it is combinatorial: connections grow as the square of systems, so three mutually-aware systems need three connections and ten need forty-five. That argument is real at ten systems and premature at three.

PickA native app or built-in connectorWhenA first-party integration exists and covers the objects and direction you need

Always try this first. A connection somebody else maintains costs less than one you maintain, even when it does less. The failure mode to watch for is scope rather than quality: most native apps sync a fixed set of objects in a fixed direction, and the moment your requirement includes a custom object, a derived value or a second direction, you are outside what the app does. Our guide to HubSpot integrations maps which connectors cover what.

PickAn integration platformWhenYou have several straightforward connections that need scheduling, retries and monitoring, and nobody wants to host anything

Integration platforms are genuinely good at breadth: hundreds of pre-built connectors, a visual builder, and operational plumbing you would otherwise write yourself. They are less good at depth, because complex logic ends up expressed in a UI rather than in code, and per-task pricing turns high volume into a recurring bill. The iPaaS guide covers where that boundary sits, and HubSpot Operations Hub is the in-house version of the same idea.

Best fitPickA custom API integrationWhenThe logic is the point: derived values, conditional writes, real volume, or business rules enforced at the moment of write

Written against both APIs directly, with one declared owner per field, idempotent writes, retries with backoff, a dead letter path for what cannot be retried, and a reconciliation pass that notices drift. It costs more up front than a connector and less than the third year of a per-task subscription at volume, and it is the only option where the rule your business actually runs on can live in the integration rather than in somebody's head. The custom HubSpot integration guide covers scope and cost.

The test that cuts through it: are you moving data, or making decisions about data? Moving is a platform problem. Deciding is a build.

Where API Integrations Break

Not the dramatic failures. The quiet ones, in rough order of how often we find them in portals we audit.

The failure modes that actually happen

  • The token was revoked and nothing said so. Somebody uninstalled an app, rotated a key, or offboarded the admin whose account authorised the connection. Writes stop, no error surfaces where anyone is looking, and the two systems drift apart for as long as it takes somebody to notice by eye. Monitoring for a successful write is the fix, not monitoring for errors.

  • Retries created duplicates. A write timed out after the record was created but before the response came back. The retry created a second one. Without an idempotency key or an external ID check before insert, every network hiccup is a potential duplicate, and duplicates are found weeks later by a salesperson calling the same person twice.

  • The batch half succeeded. Batch endpoints improve throughput and introduce partial failure. HubSpot's batch operations are limited to 100 records at a time, and a batch can come back with some records written and some rejected. Code that treats a batch as atomic loses whatever was in the rejected half, silently.

  • The read could not fetch what the write needed. HubSpot's own documentation notes that the batch read endpoint cannot retrieve associations, so a job that needs a contact and the deal it belongs to has to make a second pass. Discovering that after the design is set is what turns a one week build into a three week one.

  • The write was accepted and ignored. The most disorienting class of failure, because the API returns success. A lifecycle stage that can only move forward, a line item silently deduplicated inside a batch, a read-only calculated property: the call is valid, the response is a 200, and the value in the target is not the value you sent. Verify a sample of writes by reading them back, at least during the first week.

  • Pagination drifted mid-run. A long backfill pages through records that are being edited while it runs. Offset-based paging can skip or repeat records when the underlying set shifts. Cursor-based paging, which is what HubSpot's after parameter provides, is considerably more robust, and the search endpoint's 10,000 result cap forces you into windows anyway.

  • Somebody renamed a property. An admin edits a picklist value or an internal name, and a mapping that was correct yesterday now writes into nothing. Integrations do not get told about schema changes. The only real defences are validating the mapping on startup and alerting on a sudden collapse in successful writes.

What Separates a Script From an Integration

Every item in that list has a standard answer. The presence or absence of those answers is the entire difference between something that worked on a laptop and something that runs a business process.

ConcernA scriptA production integration
AuthToken pasted into a constantRefreshed on expires_in, alerting when refresh fails
Change detectionFull export every runWebhook signal plus a modified-since poll, with a durable watermark
WritesFire and forgetIdempotent, with an external ID recorded before the write counts as done
FailureException in a terminalRetry with backoff, then a dead letter queue a human reviews
Rate limitsDiscovered at the first 429Paced against the narrowest endpoint, honouring the limit headers
Field ownershipWhatever the code happens to writeDeclared per field, in writing, before the first line of code
CorrectnessAssumedA scheduled reconciliation that compares both sides and reports drift
ObservabilityConsole outputAlerts on staleness, not only on errors

Nothing in the right-hand column is exotic. It is the standard shape of the work, and it is why a serious integration costs more than the afternoon the first version took.

What to know before you scope a build

The numbers worth carrying into a scoping conversation, all from published documentation rather than folklore.

5 per secondthe CRM Search API's rate limit, against 190 requests per 10 seconds for the general API. Search is what a sync uses to find records changed since the last run, so the endpoint your design leans on hardest is the one metered forty times tighter. Ask anyone quoting for a sync what they are pacing against.HubSpot Developers
Out of orderHubSpot's stated guarantee on webhook notification delivery, which is that there is none. It does not guarantee notifications arrive in the order the events occurred, and directs you to each notification's occurredAt property. Any design that writes payloads in the order they land will corrupt data occasionally and unreproducibly.HubSpot Developers
24 hourshow long Stripe retains an idempotency key before pruning it, and also the window across which HubSpot spreads its ten webhook retry attempts. Both numbers say the same thing: retries are a normal part of operation rather than an exception, so every write your integration makes has to be safe to repeat.Stripe API Reference
10,000 resultsthe cap on any single CRM search query, with paging beyond it returning a 400 and a maximum page size of 200 objects. A first sync of a large portal cannot be one query. It has to be sliced into windows, and that slicing is the part of a backfill that gets discovered late.HubSpot Developers

What an API Integration Costs to Own

The build is the small number. Anyone who has run one for a year will tell you the same thing.

Up front you are paying for scope: how many objects, how many directions, how much of the mapping is a copy and how much is a decision. A one way sync of a single object between two well documented APIs is a matter of days. Bidirectional sync of three objects with conditional logic and a backfill is a different order of work, mostly because of the questions it forces rather than the code it requires.

Then there is the part that never appears in the estimate. APIs version and deprecate. Vendors change limits, and both HubSpot's token expiry and its rate limits have moved before. Your own team adds properties, renames picklist values, and builds workflows that write to fields the integration also writes to. An integration is not a thing you install, it is a thing you keep, and an unowned integration degrades in a specific and predictable way: quietly, one field at a time, until somebody stops trusting the CRM and starts keeping a spreadsheet.

Nobody notices a working integration. Everybody notices the spreadsheet that appears when people stop trusting the data.

That maintenance reality is why we productized it. StackTie builds custom integrations against the HubSpot API and your other systems directly for a fixed one-time fee, then maintains them on a flat monthly retainer, so version changes, limit changes and schema drift are somebody's job rather than nobody's. The build fee and retainer are published on the pricing page rather than quoted per call.

Not sure whether your integration is working or just quiet?

Most broken syncs do not throw errors. They stop writing, and the two systems drift for weeks before anyone notices. StackTie builds and maintains custom HubSpot integrations for a fixed fee and a flat monthly retainer. Live in 14 days or your money back. Book a free audit and we will map what is actually syncing today.

Get your blueprint

The Bottom Line

An API integration is not the API call. The call is the part that is already solved, documented and demonstrated in every quickstart. The integration is the five things around it: a credential that has to be renewed, a way to learn what changed that does not trust delivery order, a mapping that encodes decisions rather than copies, writes that are safe to repeat, and a reconciliation that catches the drift nobody saw.

The specifics move between vendors and the shape does not. Find the narrowest endpoint you depend on and design for it. Treat every event as a signal rather than as data. Decide who owns each field before anyone writes code, and write it down. Make every write idempotent, because retries are normal. And monitor for successful writes rather than for errors, because the most expensive integration failure is the one that never raises an exception.

If you can answer who owns each field, and what happens when the same record changes in both places in the same minute, the build is straightforward. If you cannot, no API was ever going to be the hard part.

Frequently Asked Questions