---
title: "How to Integrate with the QuickBooks API"
description: "Learn how to integrate the QuickBooks API into a FastAPI app for seamless accounting automation. This step-by-step guide covers developer account setup, OAuth 2.0 authentication, fetching financial data, creating invoices, handling errors, and implementing security best practices."
author: "Vivek Singh"
published: "2025-08-14T00:00+02:00"
updated: "2025-08-28T15:55:13.251Z"
url: "https://www.apideck.com/blog/how-to-integrate-with-quickbooks-api"
category: "Accounting"
tags: ["Accounting", "Guides & Tutorials"]
---

# How to Integrate with the QuickBooks API

[QuickBooks](https://quickbooks.intuit.com/) is one of the most popular accounting software, especially among small and medium-sized businesses. Its API lets you integrate QuickBooks functionality into your software so users don't have to switch between systems. Automating data transfer between systems reduces the need for manual data entry and helps ensure data consistency.

Common use cases of the [QuickBooks API](/blog/exploring-the-quickbooks-online-accounting-api) include the following:

* Bidirectional syncing of invoices and payments between QuickBooks and other business software
* Automating payroll or expense tracking for HR platforms
* Generating financial reports using real-time accounting data

In this article, you'll learn how to integrate the QuickBooks API into a [FastAPI](https://fastapi.tiangolo.com/) app to fetch financial data. The article will also cover environment management, making API requests, error handling, and security best practices to ensure the reliability of your app.

You can find the [code repository](https://github.com/vivekthedev/quickbooks-api-tut/) for this tutorial on GitHub.

For developers looking to streamline QuickBooks integration, Apideck offers a pre-built [QuickBooks connector](https://www.apideck.com/connectors/quickbooks) as part of its Unified Accounting API. This connector handles the authentication complexity, error handling, and data mapping challenges we'll explore in this article, allowing you to integrate QuickBooks alongside other accounting platforms through a single API.

## Setting Up a Developer Account

To access the QuickBooks API, you need to set up a QuickBooks developer account.

Navigate to the [QuickBooks Developer website](https://developer.intuit.com/app/developer/qbdesktop/docs/get-started/create-an-intuit-developer-account) and click **Sign Up** in the top-right corner of the page:

![Developer account webpage](https://i.imgur.com/9qbMhxu.png)

Complete the registration form, proceed to the verification step, and enter the validation code sent to your mobile number. Upon successful verification, you'll be redirected to the QuickBooks Developer account dashboard.

In the QuickBooks Developer account dashboard, click on the **My Hub** menu at the top right and select **App Dashboard** from the drop-down:

![App dashboard](https://i.imgur.com/ob4E6PZ.png)

To create your first app, click on the **+** icon in the Apps section. Enter the name of your app and select permissions. Since this is only a demonstration, you can select the accounting and payments permissions. In a production environment, only request the exact permissions your app requires to reduce risk.

After you click **Done**, a confirmation screen will appear. Click on **Show credentials** to grab the **Client ID** and **Client Secret** values. You will need these to authenticate your app with QuickBooks.

Next, you need to add a redirect URl so that the QuickBooks auth server knows the response is going to a trusted URI. To do this, click on the **Open App** button to see the App Overview page for your newly created app:

![App overview](https://i.imgur.com/wYF7X6E.png)

Click on the settings section in the left navigation pane, click on the **Redirect URIs** tab, and create a new URI entry by clicking **Add**.

Input `http://localhost:8000/callback` in the text field and click **Save**:

![Redirect URI settings](https://i.imgur.com/1S8U3LJ.jpeg)

## Setting Up a Backend with FastAPI

To connect your app to QuickBooks, you'll need a working backend application that can handle user authentication with OAuth 2.0, which you'll set up later in this tutorial.

The backend redirects users to the QuickBooks authorization page, securely stores the access tokens received after authorization, and uses them to make authenticated API calls. In this tutorial, you'll create your backend using [FastAPI](https://fastapi.tiangolo.com/).

To set up a FastAPI server, you need to have [Python 3.11 or above](https://python.org/downloads) installed on your system.

Run the following command in the terminal to start a new project:

```bash
mkdir quickbooks-app && cd quickbooks-app
python3 -m venv env
```

This command creates a new project directory named `quickbooks-app` and sets up a virtual environment inside it to isolate the project dependencies from the system.

Run the following command to activate the virtual environment:

```bash
source env/bin/activate
env\Scripts\activate # On Windows
```

Next, install the required libraries for the project:

```bash
python -m pip install "fastapi[standard]" python-dotenv intuit-oauth tenacity
```

The command above installs the following:

* [fastapi](https://fastapi.tiangolo.com/) for building backend endpoints and handling authentication redirects
* [python-dotenv](python-dotenv · PyPI) for building the environment variables in your code
* [intuit-oauth](https://pypi.org/project/intuit-oauth/), a Python SDK by Intuit, for simplifying the authentication process for QuickBooks apps
* [tenacity](https://pypi.org/project/tenacity/) for implementing retry logic in API endpoints

Once all the dependencies are installed, create a new file named `.env` and paste your app credentials into it:

```text
CLIENT_ID=your_app_client_id
CLIENT_SECRET=your_app_client_secret
REDIRECT_URI=http://localhost:8000/callback
ENVIRONMENT=sandbox
```

You will use the configuration above to make OAuth client calls from the FastAPI server.

To create server routes, create a new file named `main.py` and populate it with the following code:

```python
from fastapi import FastAPI, Request
from dotenv import load_dotenv
import requests
import json	

base_url = "https://sandbox-quickbooks.api.intuit.com/"
current_company = None
headers = {
 "Accept": "application/json",
 "Content-Type": "application/json",
}

load_dotenv()

app = FastAPI(title="QuickBooks API")

# Dictionary to store OAuth sessions
# This can be replaced with a more persistent storage solution like a database
oauth_session = {}

def init_db():
    global oauth_session
    try:
        with open("oauth_session.json", "r") as file:
            oauth_session = json.load(file)
    except FileNotFoundError:
        oauth_session = {}
        with open("oauth_session.json", "w") as file:
            json.dump(oauth_session, file)

init_db()

@app.get("/")
def root():
    """
    Root endpoint that returns a welcome message
    """
    return {
        "message": "Welcome to QuickBooks API",
        "Company" : current_company if current_company else "No company authenticated"
    }
if __name__ == "__main__":
    import uvicorn
    uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) 

```

The code above loads the `.env` file in the project and initializes a new FastAPI server, with a simple GET request endpoint that returns a welcome message. The file also initializes the variables `headers`, `oauth_session`, and  `current_company`, which you will use when making API requests. The `oauth_session` dictionary will act as a DB to store tokens.

The `init_db` function verifies if the app is already authorized by checking the `oauth_session.json` file for stored authentication details when the application starts.

Run this file to start the server:

```bash
python main.py
```

Now you can visit `http://localhost:8000/` to confirm if the server is running properly:

![Server check screen](https://i.imgur.com/HFCKkUE.jpeg)

## Setting Up Authentication

QuickBooks uses the [OAuth 2.0](https://oauth.net/2/) standard to authorize apps and let users send API calls. 

In an OAuth 2.0 flow, a client (in this case, your app) sends the user to the authorization server consent page (in this case, QuickBooks), where the user is asked to approve the permissions requested by the app. After the user grants consent, the server redirects back with an auth code. The client must make a server-to-server POST request to the authorization server's token endpoint to exchange this code for access and refresh tokens. The access tokens are short-lived, and they are checked on every API call. Refresh tokens stay on the client and are used to obtain new access tokens without login.

To keep the tokens secure, it's best to handle all API requests through a backend. This way, tokens are stored securely on the server and never exposed to the browser.

Let's see how to implement the OAuth 2.0 authentication flow using the QuickBooks API and sample data in the QuickBooks sandbox environment.

To access this sandbox, click on **My Hub** at the top right of your developer account dashboard and click on **Sandboxes**:

![Sandbox option](https://i.imgur.com/R3mxXQV.jpeg)

If you don't see a sandbox company, create a new one by clicking **Add**, selecting **QuickBooks Online Plus SKU**, and choosing your country. Click **Create** to generate a new sandbox environment.

You're now ready to authenticate your app with QuickBooks. To create an OAuth 2.0 flow in your FastAPI app, you have to make changes to the `main.py` file. First, add the following imports:

```python
from fastapi.responses import RedirectResponse
from intuitlib.client import AuthClient
from intuitlib.enums import Scopes
import os
```

You're importing `RedirectResponse` to redirect when the authorization URL is obtained, while `AuthClient` creates a new client that takes app configuration and generates the authorization URL.

To send an auth request, you need an `AuthClient` object with your client ID, client secret, redirect URI, and environment. Add the following code in the `main.py` file:

```python
​​
def get_auth_client():
    """
    Create and return an AuthClient instance using environment variables
    """

    return AuthClient(
        os.getenv("CLIENT_ID"),
        os.getenv("CLIENT_SECRET"),
        os.getenv("REDIRECT_URI"),
        os.getenv("ENVIRONMENT", "sandbox")
    )

```

Next, you need to create an endpoint that gets the authorization URL and redirects to the consent screen where you approve your app to access QuickBooks resources. Populate your server file with the following code:

```python

@app.get("/auth")
def auth():
    """
    Endpoint to redirect to QuickBooks authorization URL
    """
    scopes = [
        Scopes.ACCOUNTING,
    ]
    try:
        auth_client = get_auth_client()
        auth_url = auth_client.get_authorization_url(scopes)

        return RedirectResponse(auth_url, status_code=303)
    except Exception as e:
        return {"error": str(e)}

```

This code defines a `/auth` route handler that sends a request to the QuickBooks server to get an authorization URL. The `get_authorization_url` takes a scopes list as an argument, which tells the QuickBooks server what type of resources are intended to be used. Lastly, the application redirects to the consent screen.

Now, you need to create a redirect URL where the user will be redirected after authorization. Add the following code to the server file:

```python

@app.get("/callback")
def callback(request: Request):
    """
    Callback endpoint to handle QuickBooks authorization response
    """
    try:
        code = request.query_params.get("code")
        state = request.query_params.get("state")
        realm_id = request.query_params.get("realmId")

        if not code or not state:
            return {"error": "Missing code or state parameter"}

        auth_client = get_auth_client()
        auth_client.get_bearer_token(code, realm_id=realm_id)
        oauth_session = {
            "state": state,
            "access_token": auth_client.access_token,
            "refresh_token": auth_client.refresh_token,
            "realm_id": realm_id,
            "token_expiry": auth_client.expires_in
        }
        with open("oauth_session.json", "w") as file:
            json.dump(oauth_session, file)

        return {
            "message": "Authorization successful"
        }
    except Exception as e:
        return {"error": str(e)}

```

This code creates a `/callback` route handler that takes the `Request` object sent by the QuickBooks server and extracts `code`, `state`, and `realm_id` from it. The handler then calls the `get_auth_client` function to initialize `auth_client` and uses its `get_bearer_token` method to exchange `code` for `access_token` and `refresh_token`. These tokens are then stored in the dictionary storage.

Next, you need to create a new function that takes the current state, makes an API call, and sets the `current_company` variable to the name of the sandbox company. Add the following code in the same file:

```python

def set_current_company():

    headers.update({
        "Authorization": f"Bearer {oauth_session.get('access_token')}"
    })
    realm_id = oauth_session.get('realm_id')
    try:
        response = requests.get(
            f"{base_url}v3/company/{realm_id}/companyinfo/{realm_id}",
            headers=headers
        )
        if response.status_code == 200:
            return response.json()["CompanyInfo"]["CompanyName"]
        else:
            return None

    except Exception as e:
        print(f"Error setting current company: {str(e)}")

```

This code updates the `headers` to include the `access_token`. A GET request is sent to the `/v3/company/<realmid>/companyinfo/<realmid>` endpoint, which returns a [CompanyInfo object](https://developer.intuit.com/app/developer/qbo/docs/api/accounting/all-entities/companyinfo). If the request is successful, the code extracts and returns the company's name from the **CompanyName** field in the response.

Lastly, make the following changes in your root endpoint to verify if the authorization is working or not:

```python

@app.get("/")
def root():
    """
    Root endpoint that returns a welcome message
    """
    if oauth_session: # new
        global current_company # new
        current_company = set_current_company() # new
    return {
        "message": "Welcome to QuickBooks API",
        "Company" : current_company if current_company else "No company authenticated"
    }
```

If the `oauth_session` value was set successfully, this endpoint will call the `set_current_company` function and set the global variable `current_company` to the name obtained from the function.

Test this flow by running the following command in your terminal:

```bash
python main.py
```

With the server running, navigate to `http://localhost:8000/auth` in your browser: 

![QuickBooks consent screen](https://i.imgur.com/8h233oT.jpeg)

Select the sandbox company from the drop-down and press next:

![Auth successful screen](https://i.imgur.com/6yJyOa7.jpeg)

The token exchange was successful.

When you navigate to `http://localhost:8000/`, you can see the company name you selected:

![Updated home page](https://i.imgur.com/5Quzq0l.jpeg)

This confirms that the authorization was indeed successful and the `current_company` value was initialized.

## Fetch Customers

To get customers from QuickBooks, you need to send a GET request to `/v2/company/<realmid>/query/?query=select * from customer`. You can implement this functionality by adding the following code to your `main.py` file:

```python

@app.get("/customers")
def get_customers():
    """
    Endpoint to fetch customers from QuickBooks
    """
    if not oauth_session:
        return {"error": "Not authenticated"}

    headers.update({
        "Authorization": f"Bearer {oauth_session.get('access_token')}"
    })
    realm_id = oauth_session.get('realm_id')
    params = {
        "query": "Select * from Customer",
    }
    try:
        response = requests.get(
            f"{base_url}v3/company/{realm_id}/query",
            headers=headers,
            params=params
        )
        if response.status_code == 200:
            print("Response:", response.status_code, "Request successful")
            return response.json()["QueryResponse"].get("Customer", [])
        else:
            print("Response:", response.status_code, "Request failed")
            return {"error": "Failed to fetch customers", "status_code": response.status_code}
    except Exception as e:
        print(f"Error fetching customers: {str(e)}")
        return {"error": str(e)}

```

This code creates a route for `/customers`, which first checks if the user is authenticated. If they are, the function adds the authorization token to the headers by accessing it from the `oauth_session`. To access all the customers, the API URL takes a `query` params and executes the query on the QuickBooks server. It makes the API calls by passing the `headers` and `params` data to the `/v3/company/<realmid>/query` endpoint and accessing the **Customer** value from the JSON response.

The code also adds error handling in the form of the `try/catch` block, which catches any exception thrown by the request. The `/customers` route also logs the status of the request for debugging and monitoring purposes.

To test this code, run the server and navigate to the `http://localhost:8000/customers` endpoint. You will see the following output:

![Customer endpoint](https://i.imgur.com/NpvuHeG.jpeg)

## Handling Data Models and Common Endpoints

The QuickBooks API works with nested data, and this data needs to be carefully modeled to ensure API requests conform to QuickBooks' schema expectations. For customers, nested data could include their shipping address and billing address. Invoices and payments present a complex nesting of data, with each line item containing item information, which further includes data about item detail. Similarly, each line item in the payments object has multiple child attributes, which may become complex to handle.

[Pydantic](https://docs.pydantic.dev/latest/) models help manage this complexity. Pydantic is a Python library for defining and validating structured data. It ensures the data exchanged with QuickBooks is correctly formatted.

Let's now see how to define nested data models using Pydantic and how to use them to create and validate data for common QuickBooks endpoints, such as creating invoices. You'll also learn how to set up a route handler to fetch all transactions in your company.

Create a new file named `models.py` and populate it with the following code:

```python
from pydantic import BaseModel
from typing import List

class ItemRef(BaseModel):
    name: str
    value: str

class SalesItemLineDetail(BaseModel):
    ItemRef: ItemRef

class LineItem(BaseModel):
    DetailType: str
    Amount: float
    SalesItemLineDetail: SalesItemLineDetail

class CustomerRef(BaseModel):
    value: str

class InvoiceModel(BaseModel):
    Line: List[LineItem]
    CustomerRef: CustomerRef

```

To create an invoice item, two fields are required: `Line` and `CustomerRef`. The `Line` field is a list of `LineItem` objects, where each `LineItem` includes details such as the amount and type of detail. Within each `LineItem`, there's a nested `SalesItemLineDetail` object, which further contains an `ItemRef` object holding the item's `name` and `value`. This structure allows for a clear and organized representation of each item included in the invoice.

Next, you need to create an endpoint that sends a POST request to the QuickBooks API with `InvoiceData` and returns the created invoice. Open your `main.py` file and add the following import at the top:

```python
from models import InvoiceModel
```

The `InvoiceModel` will be used to validate incoming JSON data in your endpoint.

To create the `/invoices/create` route, append your file with the following code:

```python
@app.post("/invoices/create")
def create_invoice(invoice: InvoiceModel):
    """
    Endpoint to create a new invoice in QuickBooks
    """
    if not oauth_session:
        return {"error": "Not authenticated"}

    headers.update({
        "Authorization": f"Bearer {oauth_session.get('access_token')}"
    })
    realm_id = oauth_session.get('realm_id')
    try:
        response = requests.post(
            f"{base_url}v3/company/{realm_id}/invoice",
            headers=headers,
            json=invoice.model_dump()
        )
        if response.status_code == 200:
            return response.json()
        else:
            return {"error": "Failed to create invoice", "status_code": response.status_code}
    except Exception as e:
        print(f"Error creating invoice: {str(e)}")
        return {"error": str(e)}

```

The above route first checks if the user is authenticated; if it is, it adds the auth token to the headers and sends the POST request to the `/v3/company/<realmid>/invoice` API endpoint. The `InvoiceModel` data received is used as the request body to the `/invoices/create` route. If the request sends a 200 status code, the invoice was successfully created, and the route returns the created invoice.

Since this is a POST request, you cannot directly send POST data from the browser. FastAPI provides a built-in API testing feature called Swagger UI, which lets you send POST requests and test your endpoint from the browser.

To test this code, make sure your server is running, then navigate to `http://localhost:8000/docs`. This opens the Swagger UI window, which lists all the endpoints in your app:

![Swagger UI window](https://i.imgur.com/h3XXKiq.jpeg)

In the Swagger UI window, click on the POST `/invoices/create` endpoint, and then click **Try it out**:

![Create invoices endpoint](https://i.imgur.com/5xLSgy3.png)

Edit the default JSON object text box to include the necessary nested data that conforms to the `InvoiceModel` you created earlier:

```json
{
    "Line": [
        {
            "DetailType": "SalesItemLineDetail",
            "Amount": 100.0,
            "SalesItemLineDetail": {
                "ItemRef": {
                    "name": "Services",
                    "value": "1"
                }
            }
        }
    ],
    "CustomerRef": {
        "value": "1"
    }
}

``` 

Click on **Execute** for the Swagger UI to send the POST request to the `/invoices/create` endpoint. Scroll down to see the response returned by the endpoint:

![POST response](https://i.imgur.com/D7nnu4E.jpeg)

The response was successful, and an invoice object was returned as JSON.

Let's now see how to set up a route that handles the `/transactions` endpoint. This route will return all the transactions in your sandbox company.

Append the following code to the `main.py` file:

```python

@app.get("/transactions")
def get_transactions():
    """
    Endpoint to fetch transactions from QuickBooks
    """
    if not oauth_session:
        return {"error": "Not authenticated"}

    headers.update({
        "Authorization": f"Bearer {oauth_session.get('access_token')}"
    })
    realm_id = oauth_session.get('realm_id')
    try:
        response = requests.get(
            f"{base_url}v3/company/{realm_id}/reports/TransactionList",
            headers=headers,
        )
        if response.status_code == 200:
            return response.json()
        else:
            return {"error": "Failed to fetch transactions", "status_code": response.status_code}
    except Exception as e:
        print(f"Error fetching transactions: {str(e)}")
        return {"error": str(e)}
```

This code sends a GET request to `/v3/company/<realmid>/TransactionList` to obtain all the transactions in the company. If the request was successful, the list of transactions is returned as JSON.

Test this endpoint by navigating to `http://localhost:8000/transactions`:

![Transaction list](https://i.imgur.com/whm1ZvB.jpeg)

You will see that all the transactions in your sandbox company have been returned.

## Handling Errors and Rate Limits

When working with APIs, error handling and rate limit management prevent app crashes or API lockouts. 

APIs can throw various errors like 401 Unauthorized, 429 ResourceExhausted, or 500 Internal Server Error. An app making API calls should be able to handle all these errors. In case of any failures, the app should implement a retry logic with a delay to prevent overwhelming the system and allow time for temporary issues to resolve.

One common retry logic is exponential backoff, which waits progressively longer after each failure. Spacing retries reduces load on the server and gives it time to recover.

Logging is another great strategy to triage issues and pinpoint the exact source of bugs easily. It cuts the mean time to resolution by a significant amount.

To implement these strategies in your code, import the following modules into the `main.py` file:

```python
from tenacity import (
    retry,
    stop_after_attempt,
    wait_exponential,
    RetryError,
    before_sleep_log,
)
import logging
logging.basicConfig(level=logging.INFO)

logger = logging.getLogger(__name__)
```

This code imports several functions from tenacity to implement exponential backoff retry logic. It also imports a logging library and creates a logger variable, which will log the status of your API requests.

Let's create a retrying logic for the `/customers` endpoint. Add the following code to the `main.py` file just above the `get_customers` route:

```python
@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=5),
    before_sleep=before_sleep_log(logger, logging.INFO),
)
def fetch_customers_requests(realm_id: str, headers: dict, params: dict = None):
    """
    Fetch customers from QuickBooks with retry logic
    """
    logger.info(f"Fetching customers for realm_id: {realm_id} with params: {params}")
    response = requests.get(
        f"{base_url}v3/company/{realm_id}/query", headers=headers, params=params
    )
    if response.status_code == 200:
        print("Response:", response.status_code, "Request successful")
        return response.json()["QueryResponse"].get("Customer", [])
    else:
        print("Response:", response.status_code, "Request failed")
        raise Exception(f"Failed to fetch customers: {response.status_code}")
```

In this code, `fetch_customers_requests` calls the QuickBooks API to get all the customers in your company. This function is decorated with a `retry` decorator from tenacity to handle errors automatically when calling the API. This decorator is configured to retry the request up to three times. Between each attempt, it waits for a certain amount of time, starting from one second and increasing up to a maximum of five seconds. Between each retry, the code logs the attempt using `before_sleep_log`.

Next, modify the `/customers` route to use this function with retry logic:

```python

@app.get("/customers")
def get_customers():
    if not oauth_session:
        return {"error": "Not authenticated"}

    headers.update({"Authorization": f"Bearer {oauth_session.get('access_token')}"})
    realm_id = oauth_session.get("realm_id")
    params = {
        "query": "Select * from Customer",
    }
    try:
        customers = fetch_customers_requests(realm_id, headers, params)
        return customers
    except RetryError as e:
        print(f"Retry failed: {str(e)}")
        return {"error": "Failed to fetch customers after retries", "details": str(e)}
    except Exception as e:
        print(f"Error fetching customers: {str(e)}")
        return {"error": str(e)}
```

This code calls the `fetch_customers_requests` function to send the API request. The `/customers` route prepares the request parameters and calls the function.

To test this retry behavior, simulate a failed request by altering the authorization header:

```python
# In /customer route handler remove 'n' from Authorization
headers.update({"Authorizatio": f"Bearer {oauth_session.get('access_token')}"})
```

Now, start your server and navigate to `http://127.0.0.1:8000/customers`. You will see the following logs in the terminal:

```bash
INFO:main:Fetching customers for realm_id: 9341455125556714 with params: {'query': 'Select * from Customer'}
Response: 401 Request failed
INFO:main:Retrying main.fetch_customers_requests in 1.0 seconds as it raised Exception: Failed to fetch customers: 401.
INFO:main:Fetching customers for realm_id: 9341455125556714 with params: {'query': 'Select * from Customer'}
Response: 401 Request failed
INFO:main:Retrying main.fetch_customers_requests in 2.0 seconds as it raised Exception: Failed to fetch customers: 401.
INFO:main:Fetching customers for realm_id: 9341455125556714 with params: {'query': 'Select * from Customer'}
Response: 401 Request failed
Retry failed: RetryError[<Future at 0x109697750 state=finished raised Exception>]
```

As shown in the logs, the function attempts the request three times with increasing wait times. After the third failure, it raises a `RetryError`, signaling that the retries were exhausted.

## Security Best Practices

Whenever you work with sensitive financial data, keep the following security best practices in mind to prevent data breaches, unauthorized access, and violations of compliance. 

Environment variables must be stored securely on the server; they shouldn't be hard-coded. Use the `.env` file of a secrets manager service like [AWS](https://aws.amazon.com/secrets-manager/) or [Pulumi ESC](https://www.pulumi.com/product/secrets-management/) to keep credentials separate from code and reduce the risk of accidentally pushing secrets to version control.

Tokens provide full access to company financial data, so they must be encrypted before storage. Use strong encryption algorithms, such as [AES-256](https://www.progress.com/blogs/use-aes-256-encryption-secure-data), and ensure encryption keys are stored securely.

[SOC 2](https://www.imperva.com/learn/data-security/soc-2-compliance/) compliance shows that your app follows enterprise-grade security, confidentiality, and privacy. Compliance includes a data audit trail, token management, adding granular permissions like RBAC (role-based access control), and automated data lifecycle management that handles data from creation to secure destruction without manual intervention.

## Conclusion

In this article, you learned how to integrate the QuickBooks API into your application and access financial data from your app efficiently and securely.

If QuickBooks is one of several accounting solutions you need to integrate with, consider Apideck's [unified accounting API](https://www.apideck.com/accounting-api). It supports multiple accounting platforms, including [QuickBooks](https://www.apideck.com/connectors/quickbooks), with a single integration. Apideck maintains SOC 2 compliance and offers real-time data with no caching layer, ensuring customers receive data without any caching delay.

You can [try out Apideck for free](https://www.apideck.com/signup).
