Your warehouse marked a batch of orders as fulfilled on Monday morning. Shopify never heard about some of them. No error appeared anywhere. The tracking emails did not go out, and the first signal was a customer asking where the parcel was. Shopify retries a failed webhook up to eight times in a four hour period, then gives up.
That is the API layer failing quietly. Not the workflow tool, not the model, the wiring underneath both. This guide is about that wiring, and about the day the published limits bite.
API automation means two systems trade records directly over their published interfaces, so an order is entered once and read everywhere. It works when three things are true: every write is safe to repeat, every call stays inside the published rate limit, and a daily job re-checks what the live events missed. Miss any one and records vanish without an error.
This guide is for the operator whose orders get typed twice
You run one Shopify or Shopify Plus store. An order lands, and a person carries it somewhere else: into an ERP, a warehouse portal, or the spreadsheet finance actually trusts. You are not writing the code. You get the call when a record does not arrive.
An API, an application programming interface, is the published set of requests a system will accept and the answers it promises to give. It is a contract. The rate limits, the retry counts and the field names in that contract are the real constraints on any automation you buy, whatever the tool on top is called.
You will also see the word hyperautomation. It is an analyst label for connecting several systems at once rather than one at a time. It names no mechanism, so it will not appear again here. The mechanisms below are what a vendor should be able to answer on a call. Having that layer designed and owned, rather than assembled from app defaults, is an ecommerce systems integration engagement.
This is not for you if you have no live store yet, or if your problem is choosing a workflow tool. Picking the tool is a smaller decision than the ones here, and it comes second.
Shopify meters you in cost points, so a backfill needs a budget
Shopify does not count requests on its GraphQL Admin API. It counts the cost of what you asked for. Every query is priced before it runs, and the price comes out of a bucket that refills at a fixed rate. Three published numbers decide whether a job finishes or stalls.
| Published figure | Value | What it changes for your build |
|---|---|---|
| Restore rate, Standard plan | 100 cost points per second | A full catalogue backfill runs overnight, not in a lunch hour |
| Restore rate, Advanced plan | 200 cost points per second | Roughly double the throughput, same query design |
| Restore rate, Shopify Plus | 1,000 cost points per second | Room to run bulk jobs beside live traffic |
| Restore rate, Enterprise | 2,000 cost points per second | Volume stops being the binding constraint |
| Single query maximum cost | 1,000 points | Paginate. One giant read is rejected on every plan |
| Default mutation cost | 10 points | 1,000 order writes cost about 10,000 points before overhead |
| Input array maximum | 250 items | Batches are capped, so plan the loop |
| Pagination cap | 25,000 objects | Past this, you need a different read strategy |
Those figures come from Shopify’s GraphQL Admin API rate limits and its API usage limits pages, both read on 21 September 2026.
Ask your developer or your vendor one question: does the code read throttleStatus before it fires the next call? Shopify returns maximumAvailable, currentlyAvailable and restoreRate on every response. Code that reads those three fields paces itself. Code that ignores them hits the THROTTLED error, which Shopify describes as similar to a 429 Too Many Requests.
The shape of the fix is boring. Bulk work goes in a paced queue, and customer-facing work goes ahead of it. A migration that saturates the bucket at 11am slows the fulfilment writes your customers are waiting on.
Webhooks tell you sooner, scheduled reads tell you reliably
A webhook is a message the platform sends when something happens, such as an order being paid. Polling is the opposite: your system asks on a schedule whether anything changed. Most vendors present this as a choice. It is not.
| Criterion | Webhook | Scheduled read |
|---|---|---|
| Delay before you know | Seconds | As long as your interval |
| Response window you get | 5 seconds per attempt | None, you control the pace |
| Behaviour when your endpoint is down | Up to 8 retries in four hours, then gone | Next run picks it up |
| Ordering | Not guaranteed within or across topics | You control the order |
| Cost against the rate limit | None on receipt | Every run costs points |
| Right job | Reacting to a paid order now | Proving nothing was lost since yesterday |
Both webhook figures come from Shopify’s webhook troubleshooting guide, read on 21 September 2026. The ordering warning is explicit in Shopify’s webhook best practices, also read that day: Shopify does not guarantee ordering within a topic, or across topics for the same resource. A products/update can reach you before the products/create it belongs to.
That is why the five second window matters. Your endpoint should accept the message, write it to a queue, and answer. It should not do the work while Shopify waits. Processing inside the window is why a healthy integration starts dropping events under load.
A safe retry needs a key, not a hope
Retrying is how every network-facing system survives. The question is what a retry does when the first attempt succeeded and only the reply got lost. Without protection, the retry creates a second order.
Idempotency is the property that doing something twice leaves the same result as doing it once. You get it by attaching a unique key to each write, which the receiving system remembers.
Stripe documents the pattern plainly. It saves the status code and body of the first request for a given key, and returns that stored result on any retry, including a 500. Keys run to 255 characters, and Stripe may prune them once they are 24 hours old. They apply to POST only, because GET and DELETE are already idempotent (Stripe, idempotent requests, read 21 September 2026).
The same rule runs in the other direction. Shopify tells you to ignore duplicate webhook deliveries using the
X-Shopify-Webhook-Idheader. Store the ids you have already processed and drop repeats. If your integration has no such check, a single Shopify retry can create a duplicate fulfilment, a duplicate invoice, or a second charge.
Two rules follow, and both cost nothing to demand before a build starts. Every write carries a key generated from the record, not from a timestamp. Every message received gets checked against the ids you already handled.
Reconciliation catches the events that never arrived
Retries and deduplication protect the records that reach you. Neither tells you about a record that never did. After eight failed attempts in four hours, the event is gone and nothing announces its absence.
Shopify’s instruction is unambiguous: your app should not rely on receiving data from webhooks. Build a periodic job that fetches every object updated since the job last ran, using the updated_at filter. After an outage, fetch that window and feed it through the same code.
| Job | Cadence that works | What it proves |
|---|---|---|
| Live webhook handler | On every event | The customer gets their tracking email today |
| Updated-since sweep | Hourly or daily | Nothing was silently dropped since the last run |
| Count comparison | Daily | Order counts in both systems agree for yesterday |
| Outage backfill | On demand, by window | The gap during downtime is closed and named |
The count comparison is the cheapest of the four and finds problems first. Ask both systems how many orders they hold for yesterday. If the numbers differ, you have a gap and a date, before a customer finds it for you.
One identity per record decides whether the joins hold
Every integration eventually asks the same question: is this the same record I already have? Get it wrong and nothing downstream recovers. The order sits in both systems twice, under two identifiers, and no report agrees with another.
The lesson is not theoretical. On The Republic, a German civic publisher whose bilingual publishing platform we have built and maintained since November 2021, German and English pages are not two unrelated records. Locale-aware models pair them through a shared translation identity, so 2 paired content locales and 21 editor-selectable block choices sit on one content tree behind one documented delivery API (The Republic, bilingual publishing platform).
The defect that proved it worth doing was a filter. Filtering only on the requested record’s own identifier could miss the matching articles in the other locale. The identifier was real and the query was valid. It asked the wrong question, because the shared key, not the row id, is what makes two records one thing.
Decide which system owns the identity of a record before you write a single call. Everything after that is either a lookup or a guess.Autonomous, integration engineering
Your version is simpler and just as sharp. Shopify has an order id and your ERP has its own. One has to be the canonical reference, written back to the other when the record is created. Matching on email, order total or date is the guess, and it fails on the first partial refund.
What breaks is the silent half write, and one person owns it
The loud failure is cheap. The call errors, the job stops, someone gets a message, and you fix it that morning.
The expensive failure is the half write. The order header reached the ERP and its line items did not. Stock moved in one system and not the other. Nothing errored and nobody was told. You find it at month end, when finance cannot make two reports agree.
Three questions catch most of these before they ship, and none needs a developer to answer.
Before any connection goes live, write down three answers. What is the state of the record if the call fails halfway through. Which channel the alert lands in, by name. Whose name is on it when that alert fires at 7pm on a Friday. If the third answer is blank, do not turn it on.
Order the build the same way every time. Name the owner of each record’s identity. Make every write repeatable with a key. Add the live event handler. Add the scheduled sweep that proves the handler is honest. Only then connect the second system.
A model has a place here, and the place is late. Reading a supplier PDF or a free text delivery note is genuinely hard for a rule. Moving a paid order into an ERP is not. If a vendor opens with the model rather than the rate limits and the reconciliation job, they have not read the contract you are asking them to build against.
API automation, answered
What is API automation, in one sentence?
It is two systems trading records directly over their published interfaces, so a record is entered once and read everywhere, instead of a person re-typing it into the second system.
How do I stop an automation creating duplicate orders?
Attach an idempotency key to every write and have the receiving system store the result against that key, which is the pattern Stripe documents. In the other direction, drop repeat webhook deliveries by checking the X-Shopify-Webhook-Id header against the ids you have already processed.
What happens if my endpoint is down when Shopify sends a webhook?
Shopify retries up to eight times in a four hour period, and each attempt gives your endpoint five seconds to respond. After that the event is gone and nothing tells you. A scheduled job that fetches everything updated since its last run is what closes that gap.
Do I need webhooks and polling, or can I pick one?
Both, in different roles. Webhooks give the customer-facing speed, and Shopify’s guidance is that an app should not rely on receiving them. The scheduled read proves nothing was lost. Note that Shopify Plus raises the restore rate to 1,000 cost points per second against 100 on Standard, but the 1,000-point single-query ceiling holds on every plan.
Where does AI actually help here?
At the unstructured edge, once the plumbing exists. Reading a supplier PDF or a free text note is a real job for a model. Moving a structured order between two systems is a rules job, and a model adds risk there rather than removing it.



