Integrating APIs: A Practical Guide for Business Teams
Someone hands you two systems and a sentence: "when a deal closes in the CRM, create the project in the delivery tool." It sounds like an afternoon. Three weeks later you are reading a changelog entry about a deprecated OAuth scope, explaining why eleven projects were created twice last Tuesday, and discovering that the webhook you trusted stopped firing nine days ago without a single error anywhere. Integrating APIs is rarely hard in the way people expect. The request is easy. Everything around the request is the work.
This guide walks the decisions in the order you face them: how to authenticate, how to read without missing records, what to do when the far end starts refusing you, when to use webhooks and when to poll anyway, and how to write without creating duplicates. Then the question most teams answer too late: whether to build the integration at all, and what it costs to keep alive a year after the person who wrote it moved teams.
What integrating APIs actually involves
An API integration is not a connection. It is a small piece of software with a durable job, and it carries every obligation software carries: credentials that expire, error handling, a definition of what "done" means, and a person who notices when it stops. Strip away the vocabulary and every integration is doing one of four things.
Reading on a schedule. Pull orders, contacts, tickets, or transactions on a repeating basis and put them somewhere else. The most common shape, and the one most likely to silently drift out of sync.
Reacting to an event. Something happens in system A and you want something in system B within seconds. Webhooks are the mechanism, and they are excellent right up until they are not.
Writing on demand. A user or process triggers a create, update, or delete in a remote system. Here the risk is not missing data, it is duplicating it.
Asking a question. A one-off lookup to enrich a record: what plan is this customer on, what is the invoice status, who owns this account.
Most real projects combine three of the four, which is why they take longer than the estimate. A "simple" CRM to finance sync usually means an event trigger, a write, a nightly reconciliation read, and a lookup to resolve the account owner. Four integrations wearing one name.
API authentication and OAuth: choose once, live with it
Authentication is the choice that will follow you around, and it is the one people make fastest.
| Method | How it works | Good for | The trap |
|---|---|---|---|
| API key | Static string in a header | Server to server, single account, internal jobs | Never expires, so it never gets rotated. If it leaks, it works forever |
| Basic auth | Username and password, base64 encoded | Legacy systems, some finance and telephony vendors | Sends real credentials on every call. Often tied to a named human's login |
| OAuth 2.0 authorization code | User approves, you get an access token plus a refresh token | Acting on behalf of a person, multi-tenant products | Refresh tokens can be single-use or revoked on password change. Handle rotation or you break at 3am |
| OAuth 2.0 client credentials | App authenticates as itself | Service accounts, back-office jobs | Scopes are set at registration. Adding one later can require re-approval |
| Signed requests (HMAC) | You sign each request with a shared secret | Payments, anything with replay risk | Clock skew and canonicalization bugs produce failures that look random |
| JWT bearer / service account | You mint and sign your own short-lived token | Google Cloud, some enterprise platforms | Key files end up in repositories |
Three rules save more time than any amount of library selection.
First, prefer a service account or app identity over a person's credentials. The single most common cause of a dead integration is that the sales ops manager whose OAuth grant powered the sync left the company and IT deprovisioned the account. Nothing in the code changed. The integration just stopped.
Second, treat refresh tokens as mutable state that must be persisted atomically. In many OAuth flows the provider rotates the refresh token on every use and invalidates the old one immediately. If two workers refresh concurrently, or if you write the new token after the process crashes, you are locked out and the only fix is a human re-approving the connection. Store tokens durably, wrap the refresh in a lock, log every rotation.
Third, request the narrowest scopes that do the job, but request them all at the start. Scope creep after launch means going back to a security reviewer who has already forgotten the project.
Reading data: pagination, filtering, and the sync window
Reading is where correctness quietly goes wrong, because a broken read returns data. It just returns the wrong data.
Pagination. Offset pagination (?page=3&per_page=100) is the easiest and the least safe: if records are created while you page, rows shift between pages and you skip some. Cursor pagination is stable under concurrent writes and is what you want. Link-header pagination, common in REST API integration work with GitHub-style services, gives you a next URL to follow until it is absent. Whatever the scheme, never stop paging because a page came back with fewer items than the page size. Stop when the API says there is no next page.
Incremental filters. Full re-reads are fine at ten thousand records and untenable at ten million, so most APIs expose an updated_since filter. Use the provider's clock, read from the response rather than your own server, because a few seconds of skew loses records at the boundary. And overlap your windows deliberately: if you last synced at 09:00, ask for changes since 08:55 and rely on idempotent writes to absorb the repeats. Losing a record is far worse than processing one twice.
Deletes. This is the omission that bites hardest. Most incremental filters return created and updated records, not deleted ones. If your destination never learns about deletions, it accumulates ghosts: contacts that unsubscribed, deals that were merged, tickets that were spam. Look for a deleted-records endpoint, an archive flag, or plan a periodic reconciliation that compares identifier sets.
If your goal is a queryable copy of a source system rather than an operational sync, you are in pipeline territory and the tradeoffs change. ETL Tools Compared: How to Pick One Without Overbuying covers when a managed connector beats bespoke code, Data Warehouse Modeling: Star Schemas, Kimball, and Grain covers what happens once that data lands, and teams inside Microsoft estates arrive at the same place through SQL Server Integration Services (SSIS): A Practical Guide.
API rate limits, retries, and backoff
Every API will eventually tell you to slow down. How you respond determines whether that is a blip or an outage.
API rate limits come in a few flavors: fixed windows that reset on the hour, sliding windows, token buckets that allow short bursts above the steady rate, and concurrency caps that limit simultaneous in-flight requests rather than total volume. Some platforms apply limits per app, some per user, some per endpoint, and a few apply all three at once. Find out which one you are hitting, because the mitigation differs: a per-endpoint limit is solved by spreading calls, a concurrency cap is solved by reducing workers.
When you get a 429, the correct behavior is boring and specific:
- Honor
Retry-Afterif present. It is the provider telling you exactly when to come back. Guessing is worse than obeying. - Otherwise back off exponentially with jitter. Wait 1s, 2s, 4s, 8s, each multiplied by a random factor around 0.5 to 1.5. Without jitter, every worker throttled at the same moment retries at the same moment and rebuilds the spike you were avoiding.
- Cap the attempts and the total wait. Five attempts or two minutes. Infinite retry loops turn a transient error into a queue that never drains.
- Distinguish retryable from terminal. Retry 429, 502, 503, 504, and connection timeouts. Do not retry 400, 401, 403, 404, or 422: the request is wrong and will be wrong the next four times too. Retrying a 401 in a tight loop is a good way to get an application key suspended.
- Send everything else to a dead letter queue. A failed record should land somewhere a human can inspect and replay it, not vanish into a log line.
Two practices prevent most throttling in the first place. Use bulk endpoints when they exist, since creating 200 records in one call instead of 200 calls is a large reduction in quota consumption. And cache reference data: product catalogs, user lists, custom field definitions, and pipeline stages change weekly at most, and refetching them on every run is pure waste.
Webhooks vs polling: real time until it silently stops
The webhooks vs polling debate has an obvious winner on paper. A webhook delivers the event in under a second and consumes no quota. Polling every five minutes means an average latency of two and a half minutes and thousands of requests a day that mostly return nothing.
Then you run webhooks in production and learn the rest of it.
Webhooks fail in ways that produce no error on your side, because the failure happens on theirs. Your endpoint returned a 500 during a deploy, the provider retried a few times over ten minutes and gave up. Your TLS certificate renewed with an intermediate the provider's client did not trust. Someone rotated the signing secret in staging and the production subscription got disabled after too many rejections. A platform auto-expires subscriptions and expects renewal. In every one of these cases your logs are clean, your dashboards are green, and data has been quietly missing for days.
Webhooks also arrive out of order, arrive more than once, and carry stale payloads. Two updates to the same record 300 milliseconds apart can land in either sequence, and a payload assembled at emission time may describe a state that has already changed by the time you process it.
The practical answer is not to choose. Use webhooks for latency and polling for correctness:
- Verify every webhook signature before parsing the body, and return 200 quickly. Do the real work on a queue, because providers time out fast and a slow handler reads as a failure.
- Deduplicate on the provider's event ID, not on the payload contents.
- If ordering matters, re-fetch the record by ID instead of trusting the payload. The webhook becomes a hint that something changed rather than a source of truth.
- Run a reconciliation sweep on a schedule that queries records changed in the last window and compares them against what you processed. This is your smoke alarm for silent failure.
- Alert on absence. If an endpoint that normally receives hundreds of events a day receives zero by mid-morning, that should page someone. Nothing else will tell you.
That last point deserves emphasis: most integration monitoring watches for errors, and the dangerous failure mode produces no errors at all. It produces silence.
Payment failure alert with a polling safety net
Payment webhook
Failure event arrives within seconds of the charge
Verify signature
Reject anything failing the signing secret check, then return 200 fast
Re-fetch the invoice
Trust the ID, not the payload, so ordering issues cannot mislead
Resolve account owner
Match the customer to the CRM record and its owner
Post to the billing channel
One message per customer with amount and next retry date
Nightly reconciliation
List failed invoices from the last 48 hours
Find unhandled events
Anything not already processed means a webhook was lost
Writing data without creating duplicates
Reads that fail lose information. Writes that fail create it, twice.
The core defense is idempotency. Many mature APIs accept an Idempotency-Key header: send the same key with a retried request and the provider returns the original result instead of performing the action again. Use it everywhere it is offered, especially for anything financial. Derive the key from something stable, such as a source record ID plus an operation name, not from a random value generated at call time, because a random value differs on the retry and defeats the mechanism.
Where idempotency keys are not offered, get the same property yourself. Upsert on an external ID: most CRMs and support tools let you write against a foreign key you control, so a repeat write updates rather than inserts. Failing that, keep a local ledger, a table mapping source record to destination record with a status column, written before the call and updated after. On restart it tells you exactly which operations are in doubt.
Then there is the timeout ambiguity, the case people forget. Your request times out after 30 seconds. Did it succeed? You do not know: the remote system may have created the record and failed to return in time. Without an idempotency key or a ledger, the safe-looking retry is what produces the eleven duplicate projects. Design for that specific moment, because it will happen during your busiest hour.
Finally, respect field semantics. Sending a null sometimes means "clear this field" and sometimes means "leave it alone," and both behaviors appear in APIs that otherwise look identical.
Build versus connect: the real decision before integrating APIs
Most teams make this decision by default rather than deliberately. Custom integration code is not expensive to write. It is expensive to own.
| Factor | Build it yourself | Connect through an existing platform |
|---|---|---|
| Time to first working version | Days to weeks per connection | Minutes if the tool is supported |
| Logic that is specific to your business | Unlimited | Constrained to what the platform models |
| Auth, token refresh, retries, pagination | You implement and maintain all of it | Handled by the platform |
| Upstream API changes | Your team's problem, on the provider's timeline | Absorbed by the platform |
| Cost profile | Engineering time, mostly after launch | Subscription, predictable |
| Who can change it later | Whoever can read the code | Often the person who asked for it |
| Right when | The logic is a differentiator, volume is high, or no connector exists | The need is routine plumbing between well-known tools |
The honest heuristic: build when the integration is the product or encodes something genuinely proprietary. Connect when it is plumbing between systems that thousands of other companies also use. Nobody has gained competitive advantage from hand-writing a Slack notifier, and the pattern that actually works there is covered in Jira Slack Integration: Alerts Your Team Will Not Mute. Wiring a CRM to email, support, and finance is a solved shape, laid out in Salesforce Integration: Connect Email, Support, and Finance, and the CRM you picked changes what is available out of the box, which is where Best CRM Software: A Shortlist by Team Size and Budget helps.
What maintenance costs a year later
Budget conversations treat integrations as capital projects. They behave like pets.
A year after launch, a typical hand-built integration has absorbed provider version changes announced only in a changelog nobody subscribed to, a credential revocation caused by a personnel change, schema drift where a renamed custom field broke the mapping or started writing into the wrong column, a volume increase that pushed it into a rate limit tier it was never tested against, and at least one outage nobody noticed until a report looked wrong.
None of these are hard to fix. All of them require someone who understands the code, and that person has usually moved on. The real cost of an integration is the fraction of an engineer permanently allocated to keeping something boring alive.
Two practices reduce the drag. Write down, in one place, every integration you run, what credential it uses, who owns it, and what it does when it fails. And instrument for silence: a heartbeat and a volume threshold on every scheduled job. Most integration outages are discovered by an angry customer, which means they were discovered late.
Where Skopx fits, and where it does not
Skopx sits firmly on the connect side of that decision. It is an AI workspace that connects to nearly 1,000 tools a company already uses, including Gmail, Slack, Stripe, HubSpot, QuickBooks, and Google Analytics, with the authentication, token refresh, pagination, and rate-limit handling already built and maintained. You ask a question in chat and get an answer with citations back to the underlying records. You get a morning brief, and an insights engine that flags risks and anomalies across those connected tools. You describe an automation in plain language and it becomes a running workflow. Bring your own AI key for any major model, with zero markup. Pricing is $5 per month for Solo and $16 per seat per month for Team, listed on the pricing page.
Now the part that matters if you are reading a guide about integrating APIs.
Skopx is not an API gateway. It does not sit in front of your services, terminate traffic, enforce quotas, or route requests. If you need rate limiting and authentication for APIs you publish, that is a gateway product and Skopx is not one.
Skopx is not an integration platform you build custom endpoints on. You cannot point it at an undocumented internal service, define a bespoke request schema, and have it become a supported connector. It works with the tools it supports. That list is large, and it is still a list.
Skopx is not a data warehouse, an ETL tool, a BI dashboard builder, or a CRM. If the goal is landing raw records in columnar storage and modeling them, use a pipeline and a warehouse. If the goal is pixel-controlled dashboards, use a BI tool.
So the split is this. If your integration is routine plumbing between well-known SaaS tools and the output is answers, alerts, or actions, connecting is faster and cheaper to own, which is the same logic behind the choices in Team Productivity Tools That Remove Work Instead of Adding It. If it touches a proprietary internal system, needs custom endpoints, or is itself the thing customers pay for, build it, and use the earlier sections here to build it properly. Teams with heavy physical-world data have a third path, closer to Retail Analytics Platforms for Brick-and-Mortar Stores.
API integration best practices: a checklist before you ship
Run this list before anything goes to production. It is short because the same failures repeat.
- Service account or app identity, never a named person's credentials
- Refresh token persisted atomically, rotation logged, secrets in a secret store
- Cursor pagination where available, following next links until absent
- Incremental filter on the provider's clock, with a deliberate overlap window
- A plan for deletes, whether an endpoint or a periodic reconciliation
Retry-Afterhonored, exponential backoff with jitter otherwise- Retryable and terminal status codes separated explicitly, dead letter queue for the rest
- Idempotency key from stable inputs, or upsert on an external ID
- Explicit handling for the timeout ambiguity case, and for null semantics on partial updates
- Alerts on silence and volume drop, not only on error rate
- A named owner, a written runbook, and logs carrying the remote record ID
Frequently asked questions
What is the difference between an API and an integration?
An API is the interface a system exposes: endpoints, authentication, and a contract about request and response shapes. An integration is the software you write or configure that uses that interface to accomplish a business outcome. The API is the provider's responsibility. The integration is yours, including credentials, error handling, monitoring, and behavior when the other side is unavailable.
How do I integrate APIs without a developer?
For common business tools, use a platform that already supports both systems. That covers a large share of routine needs: move a record when a deal closes, alert a channel when a payment fails, summarize activity each morning. Where it stops working is custom internal systems, unusual transformation logic, and anything with unique compliance requirements. Those need engineering. The mistake is not choosing one path over the other, it is starting a build project for something a supported connector already does.
Should I use webhooks or polling?
Use both. Webhooks give you low latency and low quota consumption, and they are the right primary mechanism. Polling gives you a correctness guarantee that webhooks structurally cannot, because a webhook that never arrives generates no signal on your side. The standard pattern is webhook-driven processing plus a scheduled reconciliation sweep that catches gaps. Poll-only is acceptable when latency does not matter and volume is low. Webhook-only is acceptable only if you can tolerate losing events, which is rarer than teams assume.
What is a reasonable retry strategy for REST API integration?
Retry only transient failures: 429, 502, 503, 504, and network timeouts. Honor Retry-After when it is present, otherwise use exponential backoff with jitter capped at roughly five attempts. Never retry 4xx errors other than 429, because the request is malformed and repeating it just consumes quota. Pair every retry policy with idempotency on writes, otherwise a successful call that timed out on your side gets duplicated when you retry it.
How do I know when an integration has broken?
Assume you will not be told. Instrument three things: a heartbeat confirming the job ran, a volume check that alerts when throughput drops far below normal, and a scheduled reconciliation comparing record counts between source and destination. Error-rate alerting alone misses the most damaging failure mode, an integration that stops receiving work and therefore stops producing errors. Silence should be as loud as failure.
Skopx Team
The Skopx engineering and product team