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:
create an end-user
save the end-user ID to your database
create an auth session
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