QuickBooks Online API: Complete Developer Guide for 2026 Chintan Prajapati May 21, 2026 11 min read The QuickBooks Online (QBO) API is the primary interface for programmatically interacting with Intuit’s cloud accounting platform.Whether you’re building a SaaS integration, automating invoice workflows, or syncing financial data between systems, this API is the backbone of the QuickBooks ecosystem.This guide covers everything a developer needs to go from zero to production with the QuickBooks Online API in 2026 authentication, endpoints, rate limits, webhooks, code examples, and the hard-won lessons that save you weeks of debugging.If you’re evaluating whether to build a QuickBooks integration in-house or hire a team, this guide will give you the technical depth to make that decision confidently.TL;DR — Quick Start What it is: A RESTful API for reading and writing accounting data in QuickBooks Online (invoices, customers, payments, journal entries, and more) Auth: OAuth 2.0 (authorization code flow) — no API keys, no basic auth Base URL: https://quickbooks.api.intuit.com/v3/company/{realmId}/ Sandbox URL: https://sandbox-quickbooks.api.intuit.com/v3/company/{realmId}/ SDKs: Official SDKs for Node.js, Python, Java, .NET, PHP, and Ruby Rate limits: 500 requests/minute per realm, 40 concurrent requests Fastest path to first call: Create an Intuit Developer account → create an app → use OAuth 2.0 Playground → make a GET request to /query?query=SELECT * FROM CompanyInfoQuickBooks Online API Overview: How It WorksThe QuickBooks Online API is a REST API that uses JSON for request and response payloads (XML is also supported but deprecated for most use cases).Every API call targets a specific company (realm) identified by a realmId, and all requests require a valid OAuth 2.0 access token.Architecture at a GlanceComponentDetailsProtocolHTTPS (TLS 1.2+)Data FormatJSON (recommended) or XMLAuthenticationOAuth 2.0 (Authorization Code Grant)Base URL (Production)https://quickbooks.api.intuit.com/v3/company/{realmId}/Base URL (Sandbox)https://sandbox-quickbooks.api.intuit.com/v3/company/{realmId}/VersioningMinor versions via minorversion query parameter (current: 75)Response CodesStandard HTTP (200, 400, 401, 403, 404, 429, 500, 503)Available SDKsIntuit provides official SDKs that handle OAuth token management, request signing, and serialization:LanguagePackage / RepositoryNotesNode.jsnode-quickbooks (npm)Most popular community SDK; Intuit’s official SDK available via npm intuit-oauthPythonpython-quickbooks (PyPI)Well-maintained, supports all entity typesJavaQuickBooks-V3-Java-SDKOfficial Intuit SDK, Maven-compatible.NETQuickBooks-V3-DotNET-SDKOfficial Intuit SDK, NuGet package availablePHPQuickBooks-V3-PHP-SDKOfficial Intuit SDK, Composer-compatibleRubyquickbooks-ruby (gem)Community-maintained, actively updatedHow to Authenticate with QuickBooks Online API Using OAuth 2.0The QuickBooks Online API uses OAuth 2.0 Authorization Code flow exclusively. There are no API keys or basic auth options. Here’s how the flow works:Step 1: Register Your AppGo to the Intuit Developer Portal (developer.intuit.com), create an account, and register a new app. You’ll receive: Client ID (also called Consumer Key) Client Secret (also called Consumer Secret)Configure your redirect URI this is where Intuit sends the authorization code after the user grants access.Before going live, developers should also understand the full app setup process inside the Intuit Developer Portal.Step 2: Authorization RequestRedirect the user to Intuit’s authorization endpoint:https://appcenter.intuit.com/connect/oauth2? client_id=YOUR_CLIENT_ID &redirect_uri=YOUR_REDIRECT_URI &response_type=code &scope=com.intuit.quickbooks.accounting &state=YOUR_CSRF_TOKENScopes available: com.intuit.quickbooks.accounting Full access to accounting data com.intuit.quickbooks.payment Access to payment processing openid profile email User identity informationStep 3: Exchange Authorization Code for TokensAfter the user authorizes, Intuit redirects to your redirect URI with an authorization_code. Exchange it for tokens:// Node.js — Token Exchange const axios = require("axios"); const tokenResponse = await axios.post( "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer", new URLSearchParams({ grant_type: "authorization_code", code: authorizationCode, redirect_uri: YOUR_REDIRECT_URI, }), { headers: { Authorization: `Basic ${Buffer.from( `${CLIENT_ID}:${CLIENT_SECRET}` ).toString("base64")}`, "Content-Type": "application/x-www-form-urlencoded", }, } ); const { access_token, refresh_token, expires_in } = tokenResponse.data; // access_token expires in 1 hour (3600s) // refresh_token expires in 100 daysStep 4: Make Authenticated API CallsInclude the access token as a Bearer token in every request:const response = await axios.get( `https://quickbooks.api.intuit.com/v3/company/${realmId}/companyinfo/${realmId}`, { headers: { Authorization: `Bearer ${access_token}`, Accept: "application/json", }, } );Token Lifecycle Access token: Valid for 1 hour (3,600 seconds) Refresh token: Valid for 100 days. Use it to get a new access token before it expiresCritical: If the refresh token expires, the user must re-authorize. Store refresh tokens securely and implement proactive refresh logicQuickBooks Online API Endpoints for Invoices, Customers, Payments & BillsEvery endpoint follows the pattern: GET|POST /v3/company/{realmId}/{entity}. Here are the most-used endpoints:EntityCreateReadUpdateDeleteQueryInvoicePOST /invoiceGET /invoice/{id}POST /invoicePOST /invoice?operation=deleteGET /query?query=SELECT * FROM InvoiceCustomerPOST /customerGET /customer/{id}POST /customer— (deactivate only)GET /query?query=SELECT * FROM CustomerPaymentPOST /paymentGET /payment/{id}POST /paymentPOST /payment?operation=deleteGET /query?query=SELECT * FROM PaymentBillPOST /billGET /bill/{id}POST /billPOST /bill?operation=deleteGET /query?query=SELECT * FROM BillBill PaymentPOST /billpaymentGET /billpayment/{id}POST /billpaymentPOST /billpayment?operation=deleteGET /query?query=SELECT * FROM BillPaymentJournal EntryPOST /journalentryGET /journalentry/{id}POST /journalentryPOST /journalentry?operation=deleteGET /query?query=SELECT * FROM JournalEntryAccountPOST /accountGET /account/{id}POST /account— (deactivate only)GET /query?query=SELECT * FROM AccountVendorPOST /vendorGET /vendor/{id}POST /vendor— (deactivate only)GET /query?query=SELECT * FROM VendorItemPOST /itemGET /item/{id}POST /item— (deactivate only)GET /query?query=SELECT * FROM ItemPurchase OrderPOST /purchaseorderGET /purchaseorder/{id}POST /purchaseorderPOST /purchaseorder?operation=deleteGET /query?query=SELECT * FROM PurchaseOrderImportant notes on CRUD operations: Update = Full object replacement. You must send the complete entity object including the SyncToken (optimistic locking). Partial updates are not supported. Delete is only available on transaction entities (invoices, payments, bills). Name-list entities (customers, vendors, items, accounts) can only be deactivated by setting Active: false.Query language uses a SQL-like syntax. Example: SELECT * FROM Invoice WHERE TotalAmt > ‘1000’ ORDERBY MetaData.CreateTime DESC MAXRESULTS 100QuickBooks Online API Rate Limits and Throttling RulesUnderstanding rate limits is critical for production integrations. Here are the current limits as of 2026:Limit TypeThresholdScopeRequests per minute500Per realmId (company)Concurrent requests40Per realmId (company)Batch size30 entities per batch requestPer API callIf your integration handles high transaction volumes, review the deeper breakdown of QuickBooks API rate limits and limitations before designing sync logic.Handling 429 (Too Many Requests) ErrorsWhen you hit rate limits, the API returns a 429 status. Implement exponential backoff with jitter:async function apiCallWithRetry(fn, maxRetries = 5) { for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await fn(); } catch (error) { if (error.response?.status === 429 && attempt < maxRetries - 1) { const baseDelay = Math.pow(2, attempt) * 1000; const jitter = Math.random() * 1000; await new Promise((r) => setTimeout(r, baseDelay + jitter)); continue; } throw error; } } }Pro tips for staying under limits: Use batch operations where possible (up to 30 entities per batch) Use Change Data Capture (CDC) instead of polling individual entities: GET /cdc?entities=Invoice,Customer&changedSince=2026-01-01T00:00:00Z Cache frequently-read reference data (Chart of Accounts, Items, Tax Codes) locally Implement request queuing with rate limiting in your application layerCommon QuickBooks Online API Issues Developers Should AvoidWe’ve built dozens of QuickBooks integrations at Satva Solutions. Here are the top gotchas that trip up developers:1. SyncToken ConflictsEvery entity has a SyncToken that increments on each update. If you send a stale SyncToken, the API returns a 400 error. Always fetch the latest entity before updating.2. No Partial UpdatesUnlike modern APIs (e.g., PATCH support), the QBO API requires you to send the entire entity object on every update. Missing fields will be set to null.This is the single most common source of data loss in QBO integrations. (Fix common QuickBooks Online Accounting API errors, 2026)These limitations are not just theoretical. Missing fields and unsupported objects often require practical workarounds in real-world QuickBooks integrations.3. Sparse Updates MisconceptionIntuit documents a sparse=true parameter, but it has significant limitations.It works inconsistently across entity types, and relying on it is risky. Our recommendation: always send the full object.4. Currency and Locale IssuesMulti-currency companies require extra handling. You must specify CurrencyRef on transactions, and exchange rates are not automatically applied.If you’re building for international users, plan for this complexity upfront.5. Sandbox vs. Production GapsThe sandbox environment doesn’t perfectly mirror production behavior. Certain edge cases (webhooks, specific error codes, rate limiting behavior) may differ.Always perform thorough testing in production with a test company.For a deeper dive, read our full guide: Top 5 QuickBooks API Limitations to Know Before Developing Your QBO App.How to Test QuickBooks Online API in SandboxSetting Up Your Sandbox Log into the Intuit Developer Portal at developer.intuit.com Navigate to your app’s Dashboard Click “Sandbox” in the left menu — Intuit provides pre-populated sandbox companies with sample data Use sandbox credentials (Client ID + Secret from the “Development” keys section) Point API calls to: https://sandbox-quickbooks.api.intuit.com/v3/company/{realmId}/Sandbox Test DataSandbox companies come pre-loaded with sample customers, invoices, items, and accounts. You can also: Create additional test data via API calls Reset sandbox data from the developer portal Use the API Explorer in the developer portal for interactive testingTesting Best Practices Write integration tests against sandbox before deploying to production Test error handling: intentionally send malformed requests, expired tokens, and duplicate entities Verify webhook delivery using tools like ngrok or webhook.site for local development Test with multi-currency sandbox companies if your integration handles international clientsTeams planning wider accounting integrations should also compare QuickBooks with other accounting APIs before finalizing the architectureQuickBooks Online API Webhooks: Events, Setup & PayloadsQuickBooks Online webhooks notify your application when data changes, eliminating the need for constant polling.Available Webhook EventsWebhooks fire on Create, Update, and Delete operations for these entities:Account, Bill, BillPayment, Budget, Class, CreditMemo, CurrencyCustomer, Department, Deposit, Employee, Estimate, InvoiceItem, JournalEntry, Payment, Purchase, PurchaseOrderRefundReceipt, SalesReceipt, TimeActivity, Transfer, Vendor, VendorCreditWebhook Setup In the Intuit Developer Portal, navigate to Webhooks in your app settings Enter your endpoint URL (must be HTTPS) Select the entities you want to subscribe to Intuit generates a verifier token for signature validationWebhook Payload Format{ "eventNotifications": [ { "realmId": "1234567890", "dataChangeEvent": { "entities": [ { "name": "Invoice", "id": "123", "operation": "Update", "lastUpdated": "2026-03-15T10:30:00Z" } ] } } ] }Validating Webhook SignaturesAlways validate the webhook signature to ensure the payload is from Intuit:const crypto = require("crypto"); function isValidWebhookSignature(payload, signature, verifierToken) { const hash = crypto .createHmac("sha256", verifierToken) .update(payload) .digest("base64"); return hash === signature; }Important: Webhook payloads only contain entity IDs and operation types — not the full entity data. You must make a follow-up API call to fetch the updated entity.QuickBooks Online API Best Practices for Production IntegrationsError Handling Always check the Fault object in error responses for detailed error codes and messages Implement retry logic for 429 (rate limit), 500 (server error), and 503 (service unavailable) Do not retry 400 (bad request) or 401 (unauthorized) these require code or token fixes Log all API errors with full request/response bodies for debuggingPaginationThe QBO query API supports STARTPOSITION and MAXRESULTS:SELECT * FROM Invoice STARTPOSITION 1 MAXRESULTS 100 SELECT * FROM Invoice STARTPOSITION 101 MAXRESULTS 100Maximum MAXRESULTS is 1000. For large datasets, implement pagination loops and consider using CDC for ongoing sync instead.Minor Version Management Always specify the minorversion parameter in your API calls: ?minorversion=75 Pin to a specific version in production don’t use “latest” Test new minor versions in sandbox before upgrading production Monitor Intuit’s changelog for breaking changes in new minor versionsIdempotency The QBO API supports a Request-Id header for idempotent requests Use a unique UUID for each create operation to prevent duplicates on retries For production-grade systems, real-world validation, duplicate prevention, and error handling are just as important as API connectivity.This is especially important for payment and invoice creation, where duplicates cause real financial issuesSecurity Store OAuth tokens encrypted at rest (never in plain text or client-side storage) Implement token refresh proactively (before expiry, not after failure) Use HTTPS everywhere Intuit rejects non-TLS connections Scope your OAuth permissions to only what your app needsQuickBooks Online API Code Examples for DevelopersCreating an Invoice (Node.js)const axios = require("axios"); async function createInvoice(realmId, accessToken) { const invoice = { Line: [ { Amount: 150.0, DetailType: "SalesItemLineDetail", SalesItemLineDetail: { ItemRef: { value: "1", name: "Services" }, Qty: 1, UnitPrice: 150.0, }, }, ], CustomerRef: { value: "1" }, BillEmail: { Address: "customer@example.com" }, DueDate: "2026-04-15", }; const response = await axios.post( `https://quickbooks.api.intuit.com/v3/company/${realmId}/invoice?minorversion=75`, invoice, { headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": "application/json", Accept: "application/json", }, } ); return response.data.Invoice; }Querying Customers (Python)import requests def get_active_customers(realm_id, access_token, max_results=100): base_url = f"https://quickbooks.api.intuit.com/v3/company/{realm_id}" query = f"SELECT * FROM Customer WHERE Active = true MAXRESULTS {max_results}" response = requests.get( f"{base_url}/query", params={"query": query, "minorversion": "75"}, headers={ "Authorization": f"Bearer {access_token}", "Accept": "application/json", }, ) response.raise_for_status() data = response.json() return data.get("QueryResponse", {}).get("Customer", []) # Usage customers = get_active_customers(realm_id, access_token) for customer in customers: print(f"{customer['DisplayName']} - {customer['PrimaryEmailAddr']['Address']}")OAuth 2.0 Token Refreshasync function refreshAccessToken(refreshToken, clientId, clientSecret) { const response = await axios.post( "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer", new URLSearchParams({ grant_type: "refresh_token", refresh_token: refreshToken, }), { headers: { Authorization: `Basic ${Buffer.from( `${clientId}:${clientSecret}` ).toString("base64")}`, "Content-Type": "application/x-www-form-urlencoded", }, } ); const { access_token, refresh_token, expires_in } = response.data; // IMPORTANT: Store the NEW refresh_token — it rotates on each refresh // The old refresh_token is invalidated immediately return { access_token, refresh_token, expires_in }; }QuickBooks Online API FAQsIs the QuickBooks API free?Yes, the QuickBooks Online API itself is free to use. There are no per-call charges or monthly API fees. However, you need an active QuickBooks Online subscription for the company data you’re accessing. If you’re publishing an app on the Intuit Marketplace, there may be listing and revenue-sharing requirements.What languages does the QuickBooks API support?The QuickBooks Online API is language-agnostic since it’s a REST API. Intuit provides official SDKs for Java, .NET, PHP, Node.js, Python, and Ruby. You can also use any HTTP client in any programming language (Go, Rust, Swift, etc.) to interact with the API directly.How do I get a QuickBooks API key?QuickBooks doesn’t use traditional API keys. Instead, you register an app at developer.intuit.com to get OAuth 2.0 Client ID and Client Secret credentials. These are used in the OAuth 2.0 authorization flow to obtain access tokens. See our step-by-step guide to setting up an app on the Intuit Developer Portal.What are QuickBooks API rate limits?The current rate limits are 500 requests per minute per company (realmId) and 40 concurrent connections. Batch operations can include up to 30 entities per request. If you exceed these limits, you’ll receive a 429 Too Many Requests response. Implement exponential backoff with jitter to handle throttling gracefully.Can I use the QuickBooks API with Desktop?The QuickBooks Online API does not work with QuickBooks Desktop. Desktop uses a separate integration method via the QuickBooks Web Connector (QBWC) or the QuickBooks Desktop SDK, which uses XML-based messaging (qbXML). If you need to support both, you’ll need to build two separate integrations. Consider migrating Desktop clients to QuickBooks Online for API access.How do I handle OAuth 2.0 token expiration?Access tokens expire after 1 hour (3,600 seconds). Use the refresh token to obtain a new access token before the current one expires. Critical: The refresh token itself expires after 100 days and rotates on every use (the old token is invalidated). If the refresh token expires, the user must re-authorize your app. Best practice: refresh tokens proactively (e.g., at 50 minutes) and always store the new refresh token immediately.Need Help Building Your QuickBooks Integration?Satva Solutions has built 50+ QuickBooks Online integrations for SaaS companies, accounting firms, and enterprise teams. Whether you need a custom QuickBooks integration, API integration services, or AI-powered accounting automation, our team can help you ship faster and avoid the pitfalls covered in this guide. Contact us