QuickBooks Desktop Python API

Learn how to use QuickBooks Desktop or QuickBooks Enterprise from Python with Conductor, including setup, health checks, invoice sync, and reports.

If you want a QuickBooks Desktop Python API, you probably want to work with QuickBooks Desktop from Python without touching raw qbXML, SOAP callbacks, or Windows-only integration tooling.

That is exactly the right instinct.

Quick answers

  • Can I use Python with QuickBooks Desktop? Yes.

  • Can I use the same approach with QuickBooks Enterprise? Yes.

  • Do I need to work directly with qbXML? No, if you use Conductor.

  • Is there a native QuickBooks Desktop REST API for Python? Not natively. Conductor gives you the modern API layer most Python developers want.

Python is especially useful here when you are building ETL jobs, internal finance tooling, reporting pipelines, or data exports that need to touch QuickBooks Desktop without inheriting the raw Desktop stack.

Get started in Python

Initialize the client

from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])

That is the base Python client you will use for QuickBooks Desktop requests.

Example: create an end-user and auth session

If you are building a multi-user service, the typical flow is:

  1. create an end-user

  2. save the end-user ID to your database

  3. create an auth session

  4. send the user through the QuickBooks Desktop connection flow

from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])

end_user = conductor.end_users.create(
    company_name="Northwind Mechanical LLC",
    source_id="acct_48291",
    email="ap@northwind.example",
)

auth_session = conductor.auth_sessions.create(
    publishable_key=os.environ["CONDUCTOR_PUBLISHABLE_KEY"],
    end_user_id=end_user.id,
    link_expiry_mins=60 * 24,
)

print(
    {
        "conductor_end_user_id": end_user.id,
        "auth_flow_url": auth_session.auth_flow_url,
    }
)
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])

end_user = conductor.end_users.create(
    company_name="Northwind Mechanical LLC",
    source_id="acct_48291",
    email="ap@northwind.example",
)

auth_session = conductor.auth_sessions.create(
    publishable_key=os.environ["CONDUCTOR_PUBLISHABLE_KEY"],
    end_user_id=end_user.id,
    link_expiry_mins=60 * 24,
)

print(
    {
        "conductor_end_user_id": end_user.id,
        "auth_flow_url": auth_session.auth_flow_url,
    }
)
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])

end_user = conductor.end_users.create(
    company_name="Northwind Mechanical LLC",
    source_id="acct_48291",
    email="ap@northwind.example",
)

auth_session = conductor.auth_sessions.create(
    publishable_key=os.environ["CONDUCTOR_PUBLISHABLE_KEY"],
    end_user_id=end_user.id,
    link_expiry_mins=60 * 24,
)

print(
    {
        "conductor_end_user_id": end_user.id,
        "auth_flow_url": auth_session.auth_flow_url,
    }
)

This is a much more practical Python integration pattern than trying to wrap the native Desktop stack yourself.

Example: check the connection health

from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

try:
    conductor.qbd.health_check(
        conductor_end_user_id=conductor_end_user_id,
    )
    print("QuickBooks Desktop is connected.")
except Exception as e:
    code = getattr(e, "code", None)
    user_message = getattr(e, "user_facing_message", str(e))
    print({"ready": False, "code": code, "message": user_message})
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

try:
    conductor.qbd.health_check(
        conductor_end_user_id=conductor_end_user_id,
    )
    print("QuickBooks Desktop is connected.")
except Exception as e:
    code = getattr(e, "code", None)
    user_message = getattr(e, "user_facing_message", str(e))
    print({"ready": False, "code": code, "message": user_message})
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

try:
    conductor.qbd.health_check(
        conductor_end_user_id=conductor_end_user_id,
    )
    print("QuickBooks Desktop is connected.")
except Exception as e:
    code = getattr(e, "code", None)
    user_message = getattr(e, "user_facing_message", str(e))
    print({"ready": False, "code": code, "message": user_message})

That is usually the right first step before fetching or writing QuickBooks Desktop data.

Example: fetch invoices from QuickBooks Desktop

from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

invoices = conductor.qbd.invoices.list(
    conductor_end_user_id=conductor_end_user_id,
    updated_after="2026-01-01T00:00:00Z",
    limit=25,
)

for invoice in invoices.data:
    print(
        {
            "id": invoice.id,
            "ref_number": invoice.ref_number,
            "total_amount": invoice.total_amount,
            "updated_at": invoice.updated_at,
        }
    )
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

invoices = conductor.qbd.invoices.list(
    conductor_end_user_id=conductor_end_user_id,
    updated_after="2026-01-01T00:00:00Z",
    limit=25,
)

for invoice in invoices.data:
    print(
        {
            "id": invoice.id,
            "ref_number": invoice.ref_number,
            "total_amount": invoice.total_amount,
            "updated_at": invoice.updated_at,
        }
    )
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

invoices = conductor.qbd.invoices.list(
    conductor_end_user_id=conductor_end_user_id,
    updated_after="2026-01-01T00:00:00Z",
    limit=25,
)

for invoice in invoices.data:
    print(
        {
            "id": invoice.id,
            "ref_number": invoice.ref_number,
            "total_amount": invoice.total_amount,
            "updated_at": invoice.updated_at,
        }
    )

This is the kind of straightforward Python code developers actually want when they search for a QuickBooks Desktop API.

Example: export invoices into Postgres from Python

If your Python stack also owns the warehouse or reporting layer, you can pair the Conductor SDK with psycopg.

from conductor import Conductor
import os
import psycopg

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

with psycopg.connect(os.environ["POSTGRES_URL"]) as conn:
    with conn.cursor() as cur:
        invoices = conductor.qbd.invoices.list(
            conductor_end_user_id=conductor_end_user_id,
            updated_after="2026-01-01T00:00:00Z",
            limit=100,
        )

        for invoice in invoices.data:
            cur.execute(
                """
                INSERT INTO quickbooks_invoices (
                    quickbooks_invoice_id,
                    ref_number,
                    total_amount,
                    source_updated_at
                )
                VALUES (%s, %s, %s, %s)
                ON CONFLICT (quickbooks_invoice_id)
                DO UPDATE SET
                    ref_number = EXCLUDED.ref_number,
                    total_amount = EXCLUDED.total_amount,
                    source_updated_at = EXCLUDED.source_updated_at
                """,
                (
                    invoice.id,
                    invoice.ref_number,
                    invoice.total_amount,
                    invoice.updated_at,
                ),
            )
from conductor import Conductor
import os
import psycopg

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

with psycopg.connect(os.environ["POSTGRES_URL"]) as conn:
    with conn.cursor() as cur:
        invoices = conductor.qbd.invoices.list(
            conductor_end_user_id=conductor_end_user_id,
            updated_after="2026-01-01T00:00:00Z",
            limit=100,
        )

        for invoice in invoices.data:
            cur.execute(
                """
                INSERT INTO quickbooks_invoices (
                    quickbooks_invoice_id,
                    ref_number,
                    total_amount,
                    source_updated_at
                )
                VALUES (%s, %s, %s, %s)
                ON CONFLICT (quickbooks_invoice_id)
                DO UPDATE SET
                    ref_number = EXCLUDED.ref_number,
                    total_amount = EXCLUDED.total_amount,
                    source_updated_at = EXCLUDED.source_updated_at
                """,
                (
                    invoice.id,
                    invoice.ref_number,
                    invoice.total_amount,
                    invoice.updated_at,
                ),
            )
from conductor import Conductor
import os
import psycopg

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

with psycopg.connect(os.environ["POSTGRES_URL"]) as conn:
    with conn.cursor() as cur:
        invoices = conductor.qbd.invoices.list(
            conductor_end_user_id=conductor_end_user_id,
            updated_after="2026-01-01T00:00:00Z",
            limit=100,
        )

        for invoice in invoices.data:
            cur.execute(
                """
                INSERT INTO quickbooks_invoices (
                    quickbooks_invoice_id,
                    ref_number,
                    total_amount,
                    source_updated_at
                )
                VALUES (%s, %s, %s, %s)
                ON CONFLICT (quickbooks_invoice_id)
                DO UPDATE SET
                    ref_number = EXCLUDED.ref_number,
                    total_amount = EXCLUDED.total_amount,
                    source_updated_at = EXCLUDED.source_updated_at
                """,
                (
                    invoice.id,
                    invoice.ref_number,
                    invoice.total_amount,
                    invoice.updated_at,
                ),
            )

That is a realistic example for Python-based data pipelines and internal reporting workflows.

Example: fetch native reports through passthrough

from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

report = conductor.end_users.passthrough(
    conductor_end_user_id=conductor_end_user_id,
    integration_slug="quickbooks_desktop",
    body={
        "GeneralSummaryReportQueryRq": {
            "GeneralSummaryReportType": "TrialBalance"
        }
    },
)

print(report)
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

report = conductor.end_users.passthrough(
    conductor_end_user_id=conductor_end_user_id,
    integration_slug="quickbooks_desktop",
    body={
        "GeneralSummaryReportQueryRq": {
            "GeneralSummaryReportType": "TrialBalance"
        }
    },
)

print(report)
from conductor import Conductor
import os

conductor = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
conductor_end_user_id = os.environ["CONDUCTOR_END_USER_ID"]

report = conductor.end_users.passthrough(
    conductor_end_user_id=conductor_end_user_id,
    integration_slug="quickbooks_desktop",
    body={
        "GeneralSummaryReportQueryRq": {
            "GeneralSummaryReportType": "TrialBalance"
        }
    },
)

print(report)

This is useful for Python reporting jobs that need native QuickBooks accounting views.

Why Python is a strong fit

Python is especially strong for:

  • ETL jobs

  • finance data pipelines

  • warehouse syncs

  • analytics tooling

  • internal data automation

That makes Python a very natural runtime for QuickBooks Desktop export and reporting workflows, as long as the QuickBooks side is abstracted properly.

Common mistakes

Avoid these mistakes:

  • trying to wrap raw Desktop transport yourself in Python

  • treating QuickBooks Desktop like an always-available cloud source

  • skipping connection health checks

  • building exports without durable QuickBooks IDs

Frequently asked questions

Does this work for QuickBooks Enterprise too?

Yes. QuickBooks Enterprise is still part of the broader QuickBooks Desktop stack.

Should I use Python for ETL and reporting use cases?

Yes. Python is often one of the best fits for warehouse syncs, reporting jobs, and analytics-oriented integrations.

Is this a native QuickBooks Desktop Python SDK?

Not natively from QuickBooks Desktop itself. Conductor provides the Python SDK and API surface that most modern Python developers actually need.

Bottom line

If you want a QuickBooks Desktop Python API, the best approach is to use a modern Python SDK and API layer instead of building directly on qbXML, SOAP, and Web Connector mechanics.

Conductor gives Python developers a practical way to connect to QuickBooks Desktop and QuickBooks Enterprise using normal application and data-pipeline code.

Related reading