Shopify's GraphQL Payouts Query Silently Drops Payouts Once You Have More Than One Business Entity — Here's the Reconciliation Fix
If your books stopped matching Shopify's payout numbers right around the time you added a second Market or legal entity, the cause probably isn't your bookkeeping — it's the query. Querying shopifyPaymentsAccount.payouts at the top level of the GraphQL Admin API only returns payouts for your primary business entity; anything paid out through a second or archived entity vanishes from the response with no error and no warning. Here's how to confirm you're affected, the corrected query, and how to reconstruct history you already lost.
If you reconcile Shopify payouts against your bank statement by pulling data from the GraphQL Admin API, and the count came up short right around the time your store started selling into a second country or added a second legal entity, you've hit a real, narrow, and currently undocumented gap: the top-level shopifyPaymentsAccount field in the GraphQL Admin API only returns data for your primary business entity. Payouts issued through any additional entity — including one that's since been archived — don't show up in that query at all. No error, no null field, no pagination warning. The response just looks complete when it isn't.
This surfaced on the Shopify Community in mid-2026: a merchant querying shopifyPaymentsAccount.payouts got 20 results with hasNextPage: false, while the equivalent REST endpoint for the same date range returned 32. The 12 missing payouts all predated a cutoff that lined up exactly with when the store migrated to Shopify's business entities model. A follow-up thread confirmed the mechanism: once a shop uses business entities, the Payments account is scoped per entity, so the query only returns payouts for whichever entity you traversed to reach it — and the top-level shopifyPaymentsAccount field traverses to the primary entity by default. REST isn't entity-scoped, so it kept returning everything, which is exactly why the two APIs disagreed.
Why this shows up without you doing anything unusual
You don't need to deliberately set up multiple legal entities to hit this. Business entities get created automatically in a few common situations: assigning a different legal entity to a new Market when you start shipping to a new region, working with an agency or accountant who split billing/tax jurisdictions for you, or Shopify migrating an older multi-currency setup into the current entity model. Once a second entity exists — even one that's since been archived because you consolidated back down — its payout history stays attached to that entity's own ShopifyPaymentsAccount, not the primary one. Archived entities are read-only, but they are not deleted, and neither are the payouts already issued through them. They're just invisible to the query most integrations use by default.
This is the same underlying entity model discussed in Shopify Markets' localized URL architecture — Markets and business entities are configured together, so if you've done any international expansion work on Shopify this year, it's worth checking whether this applies to you even if nobody on your team remembers explicitly setting up "business entities" as a feature.
Confirm whether you're actually affected
Before rebuilding anything, check whether your store has more than one business entity, and whether any are archived:
query {
businessEntities {
id
displayName
primary
archived
}
}
If this returns exactly one entity with primary: true and no archived entities, this bug doesn't apply to you — the top-level shopifyPaymentsAccount field is already returning everything there is. If it returns more than one, or any with archived: true, cross-check a known date range against REST for the same store to see the gap directly:
curl -s "https://your-store.myshopify.com/admin/api/2026-07/shopify_payments/payouts.json?date_min=2026-01-01&date_max=2026-06-30" \
-H "X-Shopify-Access-Token: $ADMIN_API_TOKEN" | jq '.payouts | length'
Compare that count against the top-level GraphQL query for the same window. A mismatch confirms the entity-scoping issue rather than, say, a pagination bug in your own code.
The fix: traverse business entities, not the top-level field
The correct pattern is to enumerate every entity first, then pull payouts from each entity's own shopifyPaymentsAccount, instead of relying on the shop-level shortcut:
query {
businessEntities {
id
displayName
archived
shopifyPaymentsAccount {
payouts(first: 50, sortKey: ISSUED_AT, reverse: true) {
edges {
node {
id
status
issuedAt
net {
amount
currencyCode
}
}
}
pageInfo {
hasNextPage
endCursor
}
}
}
}
}
businessEntities returns a plain list, not a paginated connection — you get every entity in one call. The payouts field nested under each entity's shopifyPaymentsAccount is what's paginated, so a reconciliation script needs a pagination loop per entity, not one shared loop against a single top-level connection. This is the part that's easy to get wrong even after you've read about the bug: fixing the query shape without fixing the pagination logic just moves where the silent data loss happens.
Reconciliation script shape
A minimal Node script that won't drop entities or pages:
async function fetchAllPayouts(client) {
const entitiesRes = await client.query({
query: `{ businessEntities { id displayName archived } }`,
});
const allPayouts = [];
for (const entity of entitiesRes.data.businessEntities) {
let cursor = null;
let hasNextPage = true;
while (hasNextPage) {
const res = await client.query({
query: `query($id: ID!, $after: String) {
businessEntity(id: $id) {
shopifyPaymentsAccount {
payouts(first: 50, after: $after, sortKey: ISSUED_AT) {
edges { node { id status issuedAt net { amount currencyCode } } }
pageInfo { hasNextPage endCursor }
}
}
}
}`,
variables: { id: entity.id, after: cursor },
});
const payouts = res.data.businessEntity.shopifyPaymentsAccount?.payouts;
if (!payouts) break;
allPayouts.push(
...payouts.edges.map((e) => ({ ...e.node, entityId: entity.id, entityName: entity.displayName }))
);
hasNextPage = payouts.pageInfo.hasNextPage;
cursor = payouts.pageInfo.endCursor;
}
}
return allPayouts;
}
Tag each payout with its source entity on the way out (entityId/entityName above) — you'll need that to reconcile against the right bank account, since Shopify's native payout report doesn't label which entity a payout belongs to, and different entities can pay out to different accounts in different currencies.
When even the entity-scoped query comes up short
Occasionally an archived entity's shopifyPaymentsAccount field itself won't resolve fully — the account object exists but some fields on it return incomplete data for entities that were archived a long time ago. When that happens, ShopifyPaymentsBalanceTransaction is the fallback audit trail: every balance transaction carries an associatedPayout reference, so you can reconstruct payout-level totals by grouping transactions by that field even when the payout connection itself is uncooperative. It's more work than reading the payouts connection directly, but it doesn't depend on the entity scoping behaving correctly, since balance transactions are shop-level.
Don't lean on REST as the permanent fix
REST currently returns everything regardless of entity, which makes it tempting to just keep using REST for payout reconciliation and treat this as a GraphQL problem to avoid. Resist that. Shopify has been narrowing REST Admin API access for new API versions and pushing integrations toward GraphQL, and building a permanent reconciliation dependency on REST's current, undocumented behavior of ignoring entity scoping is a bet that behavior doesn't change. Use REST as a one-time cross-check to validate your GraphQL rewrite, then let the GraphQL query — done correctly, per-entity — be the thing you run on a schedule.
Before you add a second business entity or market
- Tell whoever owns payout reconciliation — internal finance, or an outsourced bookkeeper — before the change goes live, not after the numbers stop matching.
- Audit any custom reporting, accounting app, or internal script that queries
shopifyPaymentsAccountdirectly, and check whether it traverses entities or assumes a single account. - If you're consolidating and archiving an entity, pull and store its full payout history first — archived entities are read-only but not deleted, so the data is retrievable now; don't assume it'll be just as easy to query correctly a year later.
- Re-run the REST-vs-GraphQL count check from this post immediately after any entity change, rather than waiting for month-end reconciliation to surface a gap.
This is close cousin territory to the payout problems covered in Shopify Payments holds and verification delays — different root cause, same theme of Shopify Payments infrastructure changing underneath a merchant with no proactive notice. If you're expanding into new markets and want your reconciliation and reporting checked against how business entities actually behave rather than how the docs imply they behave, get in touch, or see what I do for Shopify stores running into integration gaps like this one.