Zuplo
APIs

QuickBooks API: Complete Developer's Guide (2026)

Nate TottenNate Totten
May 20, 2025
13 min read

Master the QuickBooks API with this developer guide covering OAuth 2.0 authentication, rate limits, endpoints, webhooks, SDKs, and production best practices.

The QuickBooks API gives developers programmatic access to the accounting features that millions of small and mid-sized businesses rely on every day. Whether you’re building an invoicing automation, syncing financial data with an ERP, or creating a custom dashboard from QuickBooks reports, this guide covers everything you need — from OAuth 2.0 authentication and rate limits to webhooks, SDKs, and production-ready code examples.

What Is the QuickBooks API?

The QuickBooks Online Accounting API is a REST API from Intuit that provides programmatic access to a QuickBooks Online company’s financial data. You can read, create, update, and delete accounting objects — Invoices, Customers, Payments, Bills, Vendors, and more — without ever logging into the QuickBooks UI.

The API uses JSON (or XML) as its data format and supports standard HTTP methods (GET, POST). There is no PUT or PATCH; all updates go through POST with a full object payload.

What Can You Build with the QuickBooks API?

  • Accounting integrations — sync transactions between your SaaS product and a customer’s books automatically
  • Invoicing automation — create and send invoices programmatically when orders are fulfilled
  • Financial dashboards — pull Profit & Loss, Balance Sheet, and AR Aging reports on demand
  • Payroll connectors — post payroll journal entries or sync employee records
  • E-commerce bridges — reconcile Shopify or WooCommerce sales against QuickBooks revenue accounts
  • Tax prep tools — export categorized transaction data for accountants

QuickBooks Online vs. QuickBooks Desktop

This guide covers QuickBooks Online (QBO), which exposes a cloud-based REST API at developer.intuit.com. QuickBooks Desktop (Enterprise, Pro, Premier) is a locally-installed product with a separate XML-based SDK (the QBXML API). The two products share no authentication or endpoint infrastructure, so make sure you’re referencing the right documentation for your use case.

Getting Started: The Intuit Developer Portal

All QuickBooks API development begins at developer.intuit.com.

Creating Your First App

  1. Sign in at developer.intuit.com (free account).
  2. Click Create an App and select QuickBooks Online and Payments.
  3. Give the app a name and select the scopes you need.
  4. Navigate to Keys & Credentials under Development Settings to find your Client ID and Client Secret.
  5. Add a Redirect URI — this is the URL Intuit will redirect to after authorization. It must match exactly what you register.

Intuit automatically provisions a sandbox QuickBooks Online company for each developer account, pre-loaded with sample data. You can create up to five sandbox companies per developer account.

QuickBooks API Authentication: OAuth 2.0

The QuickBooks API uses OAuth 2.0 exclusively — there are no API keys or basic authentication options. If you’re new to OAuth, our guide to API authentication methods covers the fundamentals.

OAuth 2.0 Scopes

The two primary scopes you’ll work with are:

  • com.intuit.quickbooks.accounting — full read/write access to QuickBooks Online accounting data. Required for most integrations.
  • com.intuit.quickbooks.payment — access to QuickBooks Payments (charge cards, ACH). Only needed if you’re processing payments directly.

Additional OpenID Connect scopes (openid, profile, email, phone, address) are available if you need to retrieve user identity information. Scopes cannot be removed after they are added to your app, so choose carefully.

Authorization Code Flow

QuickBooks uses the Authorization Code flow — the standard server-side OAuth 2.0 flow.

Step 1 — Redirect the user to Intuit’s authorization endpoint:

http
GET https://appcenter.intuit.com/connect/oauth2
  ?client_id=YOUR_CLIENT_ID
  &scope=com.intuit.quickbooks.accounting
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &state=RANDOM_CSRF_TOKEN

Step 2 — Intuit redirects back with an authorization code:

http
GET https://yourapp.com/callback
  ?code=AUTHORIZATION_CODE
  &state=RANDOM_CSRF_TOKEN
  &realmId=COMPANY_REALM_ID

Save the realmId — it uniquely identifies the QuickBooks company and is required in every API request URL.

Step 3 — Exchange the code for tokens:

Terminalbash
curl -X POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=AUTHORIZATION_CODE" \
  --data-urlencode "redirect_uri=https://yourapp.com/callback"

The response includes both your access token and refresh token:

JSONjson
{
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "REFRESH_TOKEN",
  "x_refresh_token_expires_in": 157680000,
  "access_token": "ACCESS_TOKEN"
}

Step 4 — Call the API with the access token:

Terminalbash
curl -X GET \
  "https://sandbox-quickbooks.api.intuit.com/v3/company/REALM_ID/query?query=select%20*%20from%20Customer&minorversion=75" \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Accept: application/json"

Token Lifecycle and Refresh

Access tokens expire after exactly one hour. When an access token expires, use the refresh token to get a new pair:

Terminalbash
curl -X POST https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer \
  -H "Accept: application/json" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u "YOUR_CLIENT_ID:YOUR_CLIENT_SECRET" \
  --data-urlencode "grant_type=refresh_token" \
  --data-urlencode "refresh_token=YOUR_CURRENT_REFRESH_TOKEN"

Critical refresh token behavior (updated November 2025): Refresh tokens rotate every 24–26 hours with a maximum lifetime of up to 5 years. Each time you call the token endpoint, you receive a new refresh token. You must store and use the latest refresh token on every subsequent call. If you attempt to refresh with an old, already-rotated token, the entire authorization chain is revoked and the user must re-authorize from scratch.

Token management best practices:

  • Store both tokens in a persistent, encrypted data store (not session memory)
  • Always overwrite the stored refresh token after each successful refresh
  • Implement a daily background job to proactively refresh tokens even on slow days — don’t let a refresh token go unused for more than 24 hours
  • Build a re-authorization flow for when refresh tokens expire

QuickBooks API Base URLs

Every API request is scoped to a specific company via the realmId:

  • Production: https://quickbooks.api.intuit.com/v3/company/{realmId}/
  • Sandbox: https://sandbox-quickbooks.api.intuit.com/v3/company/{realmId}/

Common Request Patterns

plaintext
# Read a single resource
GET  /v3/company/{realmId}/{entity}/{id}

# Query with SQL-like syntax
GET  /v3/company/{realmId}/query?query=SELECT * FROM Invoice WHERE TxnDate > '2026-01-01'

# Create a resource
POST /v3/company/{realmId}/{entity}

# Update a resource (full object required)
POST /v3/company/{realmId}/{entity}

# Delete a resource
POST /v3/company/{realmId}/{entity}?operation=delete

Financial reports have a separate endpoint:

plaintext
GET /v3/company/{realmId}/reports/{ReportName}

Common report names include: ProfitAndLoss, BalanceSheet, CashFlow, AgedReceivables, AgedPayables, and TrialBalance.

API Versioning

The current and only active major version is v3 — there is no plan to release a v4. Intuit uses an additive minor version system to introduce non-breaking changes. You specify a minor version by appending ?minorversion={number} to your request URL:

plaintext
GET /v3/company/{realmId}/customer/1?minorversion=75

As of August 2025, Intuit deprecated minor versions 1–74. All API requests now use minor version 75 by default, and any request specifying a lower version is treated as 75. Always include minorversion=75 (or the latest) in your requests.

QuickBooks API Rate Limits

Understanding rate limits is essential for building reliable QuickBooks integrations. The API enforces throttle limits per company (realmId):

  • Standard endpoints: 500 requests per minute per company
  • Concurrent requests: 10 maximum simultaneous requests per company
  • Batch endpoint: 120 requests per minute per company
  • Resource-intensive endpoints: 200 requests per minute

Exceeding any limit results in an HTTP 429 Too Many Requests error with error code 003001 (ThrottleExceeded). Sandbox and production environments share identical rate limits.

Handling Rate Limit Errors

When you receive a 429, implement exponential backoff:

TypeScripttypescript
async function apiRequestWithRetry(
  url: string,
  headers: Record<string, string>,
  maxRetries = 5,
): Promise<Response> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const response = await fetch(url, { headers });

    if (response.status === 429) {
      const waitTime = Math.pow(2, attempt) * 1000 + 100;
      console.log(`Rate limited. Waiting ${waitTime}ms before retry.`);
      await new Promise((resolve) => setTimeout(resolve, waitTime));
      continue;
    }

    if (!response.ok) {
      throw new Error(`QuickBooks API error: ${response.status}`);
    }

    return response;
  }

  throw new Error("Max retries exceeded");
}

Strategies to Stay Under Rate Limits

  • Use Change Data Capture (CDC) instead of polling all entities on every sync
  • Cache read responses where stale data is acceptable
  • Batch multiple operations using the batch endpoint (up to 30 per request)
  • Use webhooks for real-time event notifications instead of polling
  • Use an API gateway to enforce internal rate limits across your own services before they hit Intuit’s limits

QuickBooks API Entities

The QuickBooks Online API exposes a rich set of accounting entities. All entities support Create, Read, and Query. Most support Update. Some support Delete or Void.

Transaction Entities

  • Invoice — sales transaction billed to a customer
  • Payment — records a customer payment against one or more invoices
  • SalesReceipt — immediate sale with payment at time of purchase
  • Estimate — proposed sale (quote) sent to a customer
  • CreditMemo — credit issued to a customer
  • RefundReceipt — refund of a prior payment
  • Bill — accounts payable expense from a vendor
  • BillPayment — payment made against one or more bills
  • VendorCredit — credit from a vendor
  • Purchase — general purchase via check, credit card, or cash
  • PurchaseOrder — order sent to a vendor
  • JournalEntry — manual debit/credit accounting entry
  • Deposit — deposit to a bank account
  • Transfer — move funds between accounts

Name List Entities (Master Data)

  • Customer — a person or business that buys from you
  • Vendor — a person or business you buy from
  • Employee — a person on your payroll
  • Item — a product or service you sell or buy
  • Account — a ledger account (asset, liability, equity, income, expense)
  • Class / Department — segments for profit/loss reporting and location tracking
  • TaxCode / TaxRate — tax rates applied to transactions
  • PaymentMethod — how a customer pays (credit card, check, etc.)
  • Term — payment terms (Net 30, Due on Receipt, etc.)

Working with Entities: Code Examples

Create an Invoice (TypeScript):

TypeScripttypescript
async function createInvoice(
  accessToken: string,
  realmId: string,
  customerId: string,
  lineItems: Array<{
    itemId: string;
    amount: number;
    qty: number;
    unitPrice: number;
  }>,
) {
  const url = `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice?minorversion=75`;

  const invoice = {
    Line: lineItems.map((item) => ({
      Amount: item.amount,
      DetailType: "SalesItemLineDetail",
      SalesItemLineDetail: {
        ItemRef: { value: item.itemId },
        Qty: item.qty,
        UnitPrice: item.unitPrice,
      },
    })),
    CustomerRef: { value: customerId },
  };

  const response = await fetch(url, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${accessToken}`,
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(invoice),
  });

  if (!response.ok) {
    throw new Error(`Failed to create invoice: ${response.status}`);
  }

  const data = await response.json();
  return data.Invoice;
}

Query Customers (TypeScript):

TypeScripttypescript
async function queryCustomers(
  accessToken: string,
  realmId: string,
  searchTerm: string,
) {
  const query = encodeURIComponent(
    `SELECT * FROM Customer WHERE DisplayName LIKE '%${searchTerm}%' MAXRESULTS 100`,
  );

  const response = await fetch(
    `https://quickbooks.api.intuit.com/v3/company/${realmId}/query?query=${query}&minorversion=75`,
    {
      headers: {
        Authorization: `Bearer ${accessToken}`,
        Accept: "application/json",
      },
    },
  );

  if (!response.ok) {
    throw new Error(`QuickBooks API error: ${response.status}`);
  }

  const data = await response.json();
  return data.QueryResponse.Customer ?? [];
}

Querying Data with the QuickBooks Query Language

QuickBooks uses a SQL-like query language for the /query endpoint. The syntax is not full SQL — it supports SELECT, FROM, WHERE, ORDERBY, STARTPOSITION, and MAXRESULTS.

sql
-- Get all unpaid invoices
SELECT * FROM Invoice WHERE Balance > '0' ORDERBY TxnDate DESC

-- Find a customer by email
SELECT * FROM Customer WHERE PrimaryEmailAddr = 'jane@example.com'

-- Paginate results (page 2, 100 per page)
SELECT * FROM Invoice STARTPOSITION 101 MAXRESULTS 100

-- Count records
SELECT COUNT(*) FROM Invoice WHERE TxnDate >= '2026-01-01'

The default page size is 100 records. The maximum MAXRESULTS value is 1,000. For large datasets, paginate by incrementing STARTPOSITION.

Change Data Capture (CDC)

Instead of querying all records on every sync, use Change Data Capture to fetch only records that changed since your last sync:

plaintext
GET /v3/company/{realmId}/cdc?entities=Invoice,Customer,Payment&changedSince=2026-05-01T00:00:00-07:00&minorversion=75

CDC returns full object payloads for any entity that was created, updated, deleted, voided, or merged since the given timestamp. Changes are tracked for up to 30 days. Store the timestamp of your last successful sync and pass it as changedSince on every poll.

CDC is the recommended pattern for building sync pipelines. It dramatically reduces API call volume compared to full re-reads and helps you stay within rate limits.

Webhooks

Instead of polling CDC on a schedule, use webhooks to receive push notifications the moment something changes in a connected QuickBooks company. If you’re new to webhooks, our webhook testing guide covers the fundamentals.

Setting Up Webhooks

  1. In the Intuit Developer portal, navigate to your app and find the Webhooks section.
  2. Enter your HTTPS endpoint URL.
  3. Select the entities and event types you want to receive.
  4. Save and click Send Test Notification to verify delivery.

Webhooks support these operations: Create, Update, Delete, Merge, and Void across most accounting entities including Invoice, Payment, Customer, Bill, Vendor, and more.

Verifying Webhook Signatures

Every webhook payload includes an intuit-signature header. Verify it using HMAC-SHA256 to prevent spoofed requests:

TypeScripttypescript
import { createHmac, timingSafeEqual } from "crypto";

function verifyWebhookSignature(
  payload: string,
  signatureHeader: string,
  verifierToken: string,
): boolean {
  const expected = createHmac("sha256", verifierToken)
    .update(payload)
    .digest("base64");

  return timingSafeEqual(
    Buffer.from(expected, "utf8"),
    Buffer.from(signatureHeader, "utf8"),
  );
}

Webhook Payload Format

Webhook payloads use the CloudEvents format. A sample payload looks like:

JSONjson
{
  "specversion": "1.0",
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "source": "intuit.dsnBgbseACLLRZNxo2dfc4evmEJdxde58xeeYcZliOU=",
  "type": "qbo.invoice.created.v1",
  "datacontenttype": "application/json",
  "time": "2026-05-07T14:32:00Z",
  "intuitentityid": "42",
  "intuitaccountid": "123456789",
  "data": {
    "entityName": "Invoice",
    "entityId": "42",
    "operation": "Create",
    "lastUpdated": "2026-05-07T14:32:00Z"
  }
}

Note that webhook payloads contain only a notification — not the full entity data. Upon receiving a webhook, query the specific entity by ID to get the current state.

Batch Operations

The batch endpoint lets you combine up to 30 operations in a single HTTP request, reducing round-trips and helping stay within rate limits:

JSONjson
{
  "BatchItemRequest": [
    {
      "bId": "1",
      "operation": "create",
      "Invoice": {
        "Line": [
          {
            "Amount": 100.0,
            "DetailType": "SalesItemLineDetail",
            "SalesItemLineDetail": {
              "ItemRef": { "value": "1", "name": "Services" }
            }
          }
        ],
        "CustomerRef": { "value": "1" }
      }
    },
    {
      "bId": "2",
      "operation": "query",
      "Query": "SELECT * FROM Customer WHERE Id = '5'"
    }
  ]
}

Error Handling

Robust error handling is critical when working with financial data. QuickBooks returns structured error responses you should handle programmatically.

Common HTTP Status Codes

  • 200 — Success
  • 400 — Bad Request (malformed payload or missing required field)
  • 401 — Unauthorized (access token is missing, expired, or invalid)
  • 403 — Forbidden (token valid but lacks required scope)
  • 429 — Too Many Requests (rate limit exceeded)
  • 500 — Internal Server Error (Intuit-side issue)
  • 503 — Service Unavailable (transient outage)

Error Response Format

JSONjson
{
  "Fault": {
    "Error": [
      {
        "Message": "Duplicate Name Exists Error",
        "Detail": "The name supplied already exists.",
        "code": "6240",
        "element": ""
      }
    ],
    "type": "ValidationFault"
  },
  "time": "2026-05-07T14:32:00.000-07:00"
}

Include the x-transaction-id response header value in any support request to Intuit — it uniquely identifies every API call.

Common QuickBooks Error Codes

  • 3200 — Token expired (refresh the access token)
  • 3201 — Invalid token
  • 6240 — Duplicate name (entity with that name already exists)
  • 6000 — Required field missing
  • 4000 — Object not found
  • 003001 — ThrottleExceeded (back off and retry)

Available SDKs

Intuit maintains official SDKs for several languages:

For Python, JavaScript/Node.js, and Go, the community maintains well-regarded libraries:

QuickBooks API Pricing

A QuickBooks developer account is free, with immediate sandbox access, up to five sandbox companies, and all developer tools at no cost.

For production, Intuit’s App Partner Program (launched in 2025) introduced usage-based pricing:

  • Core (data-in) — creating or updating data (invoicing, customer creation, bill entry). These write operations are free and unlimited.
  • CorePlus (data-out) — reading or querying data (GET requests, query endpoints, reports, CDC). These are metered.

The free Builder tier includes 500,000 reads per month. Higher-volume applications need paid tiers (Silver, Gold, Platinum) with progressively higher read allotments. For exact tier limits and overage rates, see the Partner FAQ on the Intuit Developer portal.

Building Production-Ready QuickBooks Integrations

Getting API calls working in the sandbox is just the beginning. Here’s what you need for a production-grade integration.

Token Storage and Rotation

Never store OAuth tokens in plain text. Encrypt them at rest. Use an atomic write pattern when rotating refresh tokens to prevent race conditions — if two threads refresh simultaneously using the same old refresh token, one will invalidate the other. For more on securing API credentials, see our guide to API security best practices.

Error Recovery

Build a re-authorization flow for users whose refresh tokens have expired or been revoked. Surface a clear message and a re-connect button in your UI. The Intuit developer portal allows you to configure a Reconnect URL for this purpose.

Managing API Volume at Scale

As your integration grows, you’ll need to manage API volume across many connected companies. An API gateway between your application and the QuickBooks API can enforce per-company rate limits, cache GET responses, and handle retries with exponential backoff. Zuplo makes it easy to proxy outbound API calls with built-in rate limiting, caching, and request/response transformation — so you don’t have to build that infrastructure yourself.

Webhook Reliability

Webhooks can be delivered out of order or more than once. Design your webhook handler to be idempotent. Use the entity SyncToken (a version counter QuickBooks includes on every object) to detect stale updates and avoid overwriting newer data with older payloads.

Multi-Company Architecture

Each connected QuickBooks company is a separate realmId with its own token pair and rate limit bucket. Design your database schema and job queue to partition by realmId from day one.

Exploring Alternatives to the QuickBooks API

While QuickBooks dominates the small business accounting market, several alternatives offer API capabilities worth considering:

  • Xero API — a comprehensive REST API with excellent developer documentation. A strong choice for businesses already using Xero’s accounting platform.
  • FreshBooks API — well-suited for service-based businesses with strong time-tracking, project management, and client billing capabilities.
  • Sage Intacct API — targets mid-market and enterprise businesses with multi-entity consolidation and advanced financial reporting needs.

When selecting between QuickBooks and alternatives, consider not just the API’s technical capabilities but also which accounting system your target users are most likely to already use. For a deeper look at building integrations for the financial sector, see our guide on API strategies for financial services companies.

Conclusion

The QuickBooks API is a mature, well-documented platform used by thousands of production integrations. To recap the essential steps for getting your integration production-ready:

  • Register your app at developer.intuit.com and configure OAuth 2.0 scopes
  • Implement the Authorization Code flow with secure, persistent token storage
  • Use CDC and webhooks instead of polling to minimize API call volume
  • Handle rate limits with exponential backoff and internal throttling
  • Validate webhook signatures and design idempotent handlers
  • Partition your architecture by realmId for multi-company support

For enterprise-grade API management that enhances your QuickBooks integration, Zuplo’s API gateway adds authentication, rate limiting, caching, and monitoring capabilities to your financial integrations — with zero infrastructure to manage. Get started free or book a demo to see how it works.