Desktop Accounting API Python SDK
The Python client for Desktop Accounting API, a REST API for QuickBooks Desktop and QuickBooks Enterprise. Your server makes typed calls such as client.qbd.invoices.create(...), and Desktop Accounting API delivers them to your customer's company file through the QuickBooks Web Connector.
- Covers all 275 operations of API 1.0.0: QuickBooks objects and reports (
client.qbd), end users, auth sessions, request tracking, qbXML passthrough and webhook verification. - Frozen dataclass models, keyword-only arguments,
Literalenums,mypy --strictclean, a synchronous and an asyncio client. - Amounts are
decimal.Decimal, neverfloat. - Every write carries an idempotency key, retries happen only where they cannot duplicate data, and an expired QuickBooks cursor never restarts a list silently.
Documentation · API reference · Every SDK method · Examples · Changelog · Status
Install
pip install desktopaccountingapi-quickbooks-desktop
The current version is 0.2.0. To pin it exactly:
pip install "desktopaccountingapi-quickbooks-desktop==0.2.0"
uv add "desktopaccountingapi-quickbooks-desktop==0.2.0"
poetry add "desktopaccountingapi-quickbooks-desktop==0.2.0"
The distribution is desktopaccountingapi-quickbooks-desktop; the import package is desktopaccountingapi.
Requirements
- Python 3.9 to 3.14.
- Dependencies: httpx and
typing_extensions. - A server-side runtime. Secret keys must never reach a browser or a desktop app you distribute (see Authentication).
Authentication
- Sign in to the dashboard and open API keys.
- Create a secret key. Test projects issue
sk_test_...keys; production projects issuesk_live_...keys. Choose Read-only for reporting jobs and AI agents that must never change data. The full key is shown once. - Put it in the
DAAPI_SECRET_KEYenvironment variable of your server:
export DAAPI_SECRET_KEY="sk_test_..."
DesktopAccountingApi() reads DAAPI_SECRET_KEY (and DAAPI_BASE_URL, if set). You can also pass api_key=.... The SDK checks the key's format and checksum locally, so a mistyped key fails before any network call.
A secret key can read and write every connected company file in its project. Keep it on your server, in a secret manager or environment variable. Never ship it to a browser, never commit it. The API refuses browser requests from other origins on purpose. If a key leaks, revoke it in the dashboard and create a new one. See Authentication and API keys.
Quickstart
Each of your customers is an end user (eu_...) with one QuickBooks Desktop company file, connected through the Web Connector. Copy an end user ID from the dashboard's End users page, then:
from desktopaccountingapi import DesktopAccountingApi
# Reads DAAPI_SECRET_KEY. for_end_user sends Daapi-End-User-Id on every QuickBooks call.
client = DesktopAccountingApi().for_end_user("eu_01j9x4m6v4c8k2t7q0r5s3w1zb")
health = client.qbd.health_check()
print(f"QuickBooks connection: {health.status}")
for invoice in client.qbd.invoices.list(limit=10):
print(invoice.ref_number, invoice.subtotal) # subtotal is a Decimal, for example Decimal("105.50")
With asyncio:
import asyncio
from desktopaccountingapi import AsyncDesktopAccountingApi
async def main() -> None:
async with AsyncDesktopAccountingApi(end_user_id="eu_01j9x4m6v4c8k2t7q0r5s3w1zb") as client:
async for customer in client.qbd.customers.list():
print(customer.full_name, customer.balance)
asyncio.run(main())
Methods mirror the API's operation IDs in snake_case: qbd.invoices.create is client.qbd.invoices.create(...), endUsers.passthrough is client.end_users.passthrough(...), qbd.reports.generalSummary is client.qbd.reports.general_summary(...). Path parameters are positional; everything else is a keyword argument.
End users
QuickBooks Desktop operations (client.qbd.*) act on one end user's company file and send the Daapi-End-User-Id header. Set the end user on the client, scope a client with for_end_user, or pass it per call:
acme = client.for_end_user("eu_01j9x4m6v4c8k2t7q0r5s3w1zb") # shares the connection pool
acme.qbd.invoices.retrieve("7-1700000000")
client.qbd.invoices.retrieve("7-1700000000", end_user_id="eu_01j9x4m6v4c8k2t7q0r5s3w1zb")
A QuickBooks call without an end user raises DaapiError before anything is sent. Platform operations (client.end_users, client.auth_sessions, client.requests) never send the header; passthrough takes the end user from its path. Create end users and their setup links with client.end_users.create and client.auth_sessions.create; see End users.
Common workflows
List records with auto-pagination
Iterating a list walks every page. The next page is requested only when the loop needs it, so a loop that stops early never runs an extra QuickBooks query. If you hold a page for more than 2 seconds, the SDK requests the next one in the background, so slow loop bodies stay inside the QuickBooks cursor's idle window.
import datetime
for customer in client.qbd.customers.list(limit=100, updated_after=datetime.date(2026, 1, 1)):
print(customer.id, customer.full_name, customer.balance)
page = client.qbd.customers.list(limit=100).first_page() # only the first page
print(len(page.data), page.has_more, page.next_cursor)
More options, and what to do when a cursor expires, are in Pagination.
Create a record with an idempotency key
Every write sends an Idempotency-Key. Pass your own, derived from your data, so a retry after a crash or timeout returns the first result instead of creating a duplicate:
import datetime
from decimal import Decimal
from desktopaccountingapi.types import InvoiceLineCreateInput
invoice = client.qbd.invoices.create(
customer_id="80000001-1700000000",
transaction_date=datetime.date(2026, 10, 5),
ref_number="WEB-8812",
lines=[InvoiceLineCreateInput(item_id="80000005-1700000000", quantity=2, rate=Decimal("52.75"))],
idempotency_key="order-8812-invoice",
)
print(invoice.id, invoice.ref_number, invoice.subtotal) # subtotal Decimal("105.50")
Update a record with its revision number
QuickBooks rejects an update unless it carries the object's current revision_number, so concurrent edits are never overwritten. Read the object, then send its revision_number with only the fields you change:
from desktopaccountingapi import ErrorCode, IntegrationError
current = client.qbd.invoices.retrieve("7-1700000000")
try:
updated = client.qbd.invoices.update(current.id, revision_number=current.revision_number, memo="Paid by card")
print(updated.revision_number) # the new revision
except IntegrationError as error:
if error.code != ErrorCode.QBD_REVISION_NUMBER_STALE:
raise
# Someone changed the invoice after you read it. Retrieve it again, reapply your change,
# and update with the new revision_number.
A stale revision is a 409 INTEGRATION_ERROR with code QBD_REVISION_NUMBER_STALE. Nothing was changed (outcome: "not_applied"):
{
"error": {
"type": "INTEGRATION_ERROR",
"code": "QBD_REVISION_NUMBER_STALE",
"message": "The object changed since you read it; revisionNumber is out of date.",
"userFacingMessage": "This record was changed by someone else. Reload it and try again.",
"httpStatusCode": 409,
"integrationCode": "3200",
"requestId": "req_01j9x4m6v4c8k2t7q0r5s3w1zd",
"cause": "QuickBooks rejects updates that do not carry the current revision number, so concurrent edits are not lost.",
"fixes": [{ "actor": "developer", "action": "Retrieve the object, merge your change, and update with the new revisionNumber." }],
"docsUrl": "https://www.desktopaccountingapi.com/docs/errors/#qbd_revision_number_stale",
"retryable": false,
"outcome": "not_applied",
"param": null,
"details": {}
}
}
Handle errors
Errors are typed by the API's error type, and every API error carries the request ID, a message you can show your end user, the cause, concrete fixes and a link to its documentation:
from desktopaccountingapi import APIError, IntegrationConnectionError
try:
client.qbd.customers.retrieve("80000099-1700000000")
except IntegrationConnectionError as error:
# QuickBooks is closed, a dialog is open, or the Web Connector is not running: the end user has to act.
show_to_end_user(error.user_facing_message or error.message)
except APIError as error:
print(error.status, error.code, error.message, error.request_id)
print(error.cause, error.docs_url)
for fix in error.fixes:
print(f"{fix.actor}: {fix.action}")
Every class and field is listed in Errors. The error catalog documents every code.
Run a request asynchronously and get a webhook
QuickBooks only processes requests while the end user's Web Connector is running. enqueue queues a request and returns at once with a handle; the API also sends a request.succeeded or request.failed webhook when it finishes:
handle = client.qbd.invoices.enqueue.create(
customer_id="80000001-1700000000",
queue_ttl=3600,
idempotency_key="order-8813-invoice",
)
print(handle.id) # req_...; the request is queued
invoice = handle.wait(timeout=120) # the typed Invoice, or the typed error
print(invoice.ref_number)
Verify each webhook delivery with the endpoint's signing secret before you trust it. Pass the raw body, not parsed JSON:
import os
from desktopaccountingapi import WebhookVerificationError, webhooks
try:
event = webhooks.verify(raw_body, headers, os.environ["DAAPI_WEBHOOK_SECRET"])
except WebhookVerificationError:
return 400
if event.type == webhooks.EventType.REQUEST_SUCCEEDED:
print("request", event.data["id"], "succeeded")
return 204
Create webhook endpoints and copy their whsec_... signing secrets in the dashboard under Webhooks. Details: Async mode, Webhooks, and the webhooks guide.
Set timeouts and retries
patient = DesktopAccountingApi(timeout=30, max_retries=4, server_timeout=25)
patient.qbd.invoices.retrieve("7-1700000000", end_user_id="eu_01j9x4m6v4c8k2t7q0r5s3w1zb", max_retries=0)
timeout is the client's limit per HTTP attempt in seconds; total_timeout caps a whole call including retries; server_timeout is how long the API waits for QuickBooks. Reads and writes retry only when it is safe; see Retries and idempotency and Timeouts.
Configuration
| Argument | Environment variable | Default | Meaning |
|---|---|---|---|
api_key |
DAAPI_SECRET_KEY |
required | Secret key, sk_live_... or sk_test_.... Checked locally (format and checksum) before any request; a missing or malformed key raises DaapiError. |
base_url |
DAAPI_BASE_URL |
https://api.desktopaccountingapi.com |
API host. May include a path prefix. A trailing /v1 is removed, so https://api.desktopaccountingapi.com/v1 works too. |
end_user_id |
None |
Default end user for QuickBooks Desktop operations. | |
timeout |
100 |
Client-side timeout in seconds for each HTTP attempt; each retry gets a fresh one. Without total_timeout, also the time budget for waiting on requests that are still running in QuickBooks. |
|
total_timeout |
None |
Time budget in seconds for a whole call: attempts, retry backoff and the wait for a pending request. | |
max_retries |
2 |
Retries for network errors, 429 and retryable 5xx responses. |
|
server_timeout |
API default (90) | Seconds the API waits for QuickBooks before answering (Daapi-Timeout-Seconds, 1-300). |
|
default_headers |
none | Headers sent with every request. The headers the SDK manages (Authorization, Accept, Content-Type, User-Agent, Daapi-End-User-Id, Idempotency-Key, Daapi-Timeout-Seconds, Prefer) are ignored here. |
|
http_client |
new client | Your own httpx.Client (httpx.AsyncClient for the async client) for proxies, custom transports or connection limits. You close it yourself. |
|
logger |
DAAPI_LOG |
logging.getLogger("desktopaccountingapi") |
Receives request and retry diagnostics at DEBUG/INFO. It never logs keys, headers or bodies. DAAPI_LOG=debug, info, warn, error or off sets that logger's level and adds a stderr handler if it has none. |
Every method also accepts timeout, total_timeout and max_retries, QuickBooks Desktop operations accept end_user_id (or Conductor's conductor_end_user_id) and server_timeout, and writes accept idempotency_key. client.with_options(...) returns a copy with other defaults. Close the client with client.close() (await client.close()) or use it as a context manager.
Models, money and dates
Models live in desktopaccountingapi.types. Responses are frozen dataclasses; to_dict() turns one back into the API's JSON, and from_wire() parses it. Request models are keyword-only classes.
- Money and prices are
decimal.Decimaland are sent as decimal strings with their scale (Decimal("5.00")is sent as"5.00"); binary floating point is never used for them. Quantities and percentages arefloat. - Dates are
datetime.date. Timestamps are timezone-awaredatetime.datetimevalues that keep the offset QuickBooks reported (the PC's local offset). - Date-range filters such as
updated_aftertake adate, adatetimeor an ISO string. - Enums are
Literaltypes. Open enums (most response enums) also accept anystr, so values added to the API later arrive unchanged. - Responses tolerate fields added later and fields that are missing.
- Optional request fields default to
NOT_GIVENand are not sent.Nonesends JSONnull, which clears a field where the API allows it:
invoice = client.qbd.invoices.retrieve("7-1700000000")
client.qbd.invoices.update(invoice.id, revision_number=invoice.revision_number, memo=None)
Pagination
Cursor lists return a pager. Iterating it yields every item across pages. The next page is requested only when the iteration reaches it, so breaking out of a loop never sends an extra QuickBooks query. While you iterate items, a page held for more than 2 seconds makes the SDK request the next page in the background, which keeps slow loops inside the cursor's idle window. iter_pages() requests each page when you ask for it; list_all() always requests the next page as soon as a page arrives. A network error while fetching a page retries the same cursor, which returns the same page.
pager = client.qbd.invoices.list(customer_ids=["80000001-1700000000"], limit=100)
for invoice in pager: # all items
save(invoice)
page = pager.first_page() # one page
print(page.data, page.next_cursor, page.has_more, page.remaining_count, page.cursor_expires_at)
for each_page in pager.iter_pages(): # page by page
print(len(each_page.data))
everything = pager.list_all() # all items in a list
The async pager works with async for, await pager.first_page(), async for page in pager.iter_pages() and await pager.list_all().
If the iterator expires (idle too long, QuickBooks restarted, session ended), the pager raises CursorExpiredError with items_yielded, pages_served, last_id, last_updated_at and reason. It never restarts the list on its own, because records may have changed. Resume with a watermark and skip what you already have:
from desktopaccountingapi import NOT_GIVEN, CursorExpiredError
seen: set[str] = set()
try:
for customer in client.qbd.customers.list():
seen.add(customer.id)
except CursorExpiredError as error:
for customer in client.qbd.customers.list(updated_after=error.last_updated_at or NOT_GIVEN):
if customer.id not in seen:
seen.add(customer.id)
Lists without cursor pagination (accounts, classes, terms and other small lists) return the whole list. The pagination guide explains cursor lifetimes.
Errors
Every error is a DaapiError. Problems found before sending (missing key, malformed key, missing end user) raise DaapiError itself. Error responses raise APIError or the subclass for their type:
type |
Exception |
|---|---|
INVALID_REQUEST_ERROR |
InvalidRequestError (and CursorExpiredError for CURSOR_EXPIRED) |
AUTHENTICATION_ERROR |
AuthenticationError |
PERMISSION_ERROR |
PermissionDeniedError |
BILLING_ERROR |
BillingError |
RATE_LIMIT_ERROR |
RateLimitError |
INTEGRATION_CONNECTION_ERROR |
IntegrationConnectionError |
INTEGRATION_ERROR |
IntegrationError |
OUTCOME_UNKNOWN_ERROR |
OutcomeUnknownError |
INTERNAL_ERROR |
InternalError |
| any other | APIError |
APIConnectionError (with APITimeoutError) means no response arrived after all retries. RequestPendingError means a request is still running in QuickBooks when the time budget ended. WebhookVerificationError comes from webhook verification. ApiError, ApiConnectionError and ApiTimeoutError are aliases.
Conductor's names work too (see Porting from Conductor). ConductorError is DaapiError. Every error from an HTTP response is also an APIStatusError, and for its status a BadRequestError (400), NotFoundError (404), ConflictError (409), UnprocessableEntityError (422) or InternalServerError (5xx): a 404 INVALID_REQUEST_ERROR is caught by both except InvalidRequestError and except NotFoundError. status_code is an alias of status.
APIError exposes the error object field by field: status (HTTP status), type, code, message, user_facing_message, http_status_code, integration_code, request_id (from the body, else the Daapi-Request-Id header), cause, fixes (each with actor and action), docs_url, retryable, outcome, param, details and the response headers. ErrorCode and ErrorType hold a constant for every catalog code and type:
from desktopaccountingapi import ErrorCode, IntegrationConnectionError, IntegrationError
try:
client.qbd.customers.retrieve("80000001-1700000000")
except IntegrationConnectionError as error:
if error.code == ErrorCode.QBD_MODAL_DIALOG_OPEN:
show_to_end_user(error.user_facing_message or error.message)
except IntegrationError as error:
print(error.code, error.param, error.request_id, error.docs_url)
for fix in error.fixes:
print(fix.actor, fix.action)
Include the request_id when you contact support. See the error handling guide.
Retries and idempotency
Every write (create, update, delete, void, passthrough...) sends an Idempotency-Key: yours (idempotency_key=...) or a UUID generated once per call and reused on each retry of that call. The API stores the result per key for 7 days, so a retry never applies a write twice.
The SDK retries up to max_retries times (default 2) on:
- network errors before a response (connection failure, dropped connection, client timeout), for reads and writes;
429;5xxresponses withDaapi-Should-Retry: true.
It never retries when Daapi-Should-Retry is false, when the error's outcome is unknown or pending, or on a non-JSON error without that header. The delay is 0.5 s doubling to at most 8 s, with jitter; Retry-After overrides it. See the idempotency guide.
Timeouts
timeout(client side, default 100 s) limits each HTTP attempt. A retry starts a new attempt with a fresh timeout, so with retries a call can take longer thantimeout.total_timeout(client side, no default) limits the whole call: every attempt, the waits between retries, and the wait for a pending request. An attempt still running when it ends is cut off (APITimeoutError), and no retry starts that could not finish in time.server_timeout(Daapi-Timeout-Seconds) is how long the API waits for QuickBooks before answering a sync call (API default 90 s, health check 60 s, maximum 300 s).
If the API answers 504 QBD_REQUEST_TIMEOUT because the request was sent to QuickBooks but has not finished, the SDK does not send it again. It long-polls GET /v1/requests/{id} until the request finishes or the call's deadline (total_timeout, else timeout) ends, then returns the typed result, raises the request's typed error, or raises RequestPendingError with request_id (and the last request snapshot). Check the request later:
from desktopaccountingapi import RequestPendingError
try:
client.qbd.invoices.create(customer_id="80000001-1700000000", idempotency_key="order-8814-invoice")
except RequestPendingError as error:
request = client.requests.retrieve(error.request_id)
print(request.status) # still "queued" or "running"; a webhook reports the result
Async mode
Operations that run through the QuickBooks queue can be started in async mode (Prefer: respond-async). They return at once with a request handle:
handle = client.qbd.invoices.enqueue.create(customer_id="80000001-1700000000", queue_ttl=3600)
print(handle.id) # req_...; the request is queued
invoice = handle.wait(timeout=600) # long-polls; returns an Invoice or raises its typed error
current = handle.status() # current Request, no waiting
result = handle.result() # Invoice if done, typed error if failed, RequestPendingError if running
print(invoice.id, current.status, result.id)
enqueue exposes every async-capable operation except cursor lists, with the same parameters plus queue_ttl (Daapi-Queue-Ttl-Seconds). On the async client, await handle.wait(). See the request lifecycle guide.
Webhooks
Webhooks follow Standard Webhooks. webhooks.verify(raw_body, headers, secret) checks the webhook-id, webhook-timestamp and webhook-signature headers (case-insensitive, several signatures during secret rotation), rejects timestamps more than 300 seconds from the local clock in either direction (tolerance=), compares in constant time and returns a WebhookEvent (id, type, timestamp, project_id, data). The secret is accepted with or without whsec_, and no API key is needed. webhooks.verify_signature(...) checks only the signature, and client.webhooks.verify(...) is the same helper on a client. Delivery is at least once: deduplicate on event.id.
Raw responses
with_raw_response returns a typed RawResponse[T] with the HTTP status, headers and request ID next to the parsed result:
raw = client.qbd.customers.with_raw_response.retrieve("80000001-1700000000")
print(raw.status_code, raw.request_id, raw.headers.get("daapi-warnings"))
customer = raw.parse() # Customer
print(customer.full_name)
It covers every method except cursor lists.
Passthrough
Send qbXML as JSON, or raw qbXML, to an end user's QuickBooks:
result = client.end_users.passthrough("eu_01j9x4m6v4c8k2t7q0r5s3w1zb", {"CustomerQueryRq": {"MaxReturned": 5}})
xml = client.end_users.passthrough_xml(
"eu_01j9x4m6v4c8k2t7q0r5s3w1zb", "<CustomerQueryRq><MaxReturned>5</MaxReturned></CustomerQueryRq>"
)
print(result, xml)
Passthrough always sends an idempotency key, because a message other than a query is a write.
Porting from Conductor
Code written for conductor-py runs on this SDK with two edits: the import and the API key. The resource tree, method names, keyword arguments and response fields are the same, and Conductor's conductor_end_user_id= and error class names are accepted.
# Before (conductor-py):
# import conductor
# from conductor import Conductor
# client = Conductor(api_key=os.environ["CONDUCTOR_SECRET_KEY"])
import os
import desktopaccountingapi as conductor
from desktopaccountingapi import DesktopAccountingApi as Conductor
client = Conductor(api_key=os.environ["DAAPI_SECRET_KEY"])
# Everything below is unchanged Conductor code.
end_user_id = "eu_01j9x4m6v4c8k2t7q0r5s3w1zb"
client.qbd.health_check(conductor_end_user_id=end_user_id)
for invoice in client.qbd.invoices.list(conductor_end_user_id=end_user_id, limit=50):
print(invoice.ref_number, invoice.subtotal)
customer = client.qbd.customers.create(conductor_end_user_id=end_user_id, name="Acme Supply")
try:
client.qbd.invoices.retrieve("7-1700000000", conductor_end_user_id=end_user_id)
except conductor.NotFoundError:
print("No such invoice")
except conductor.RateLimitError:
print("A 429 status code was received; back off a bit.")
except conductor.APIStatusError as e:
print(e.status_code, getattr(e, "code", None), getattr(e, "user_facing_message", str(e)))
# Our richer fields are on the error too.
print(e.type, e.integration_code, e.request_id, e.cause, e.fixes, e.docs_url, e.outcome, e.retryable)
except conductor.APIConnectionError:
print("The server could not be reached")
print(customer.id)
The async client is AsyncDesktopAccountingApi (Conductor's AsyncConductor). Client options keep their Conductor names where they exist (api_key, base_url, timeout, max_retries, default_headers, http_client):
import os
import httpx
from desktopaccountingapi import DesktopAccountingApi as Conductor
client = Conductor(
api_key=os.environ["DAAPI_SECRET_KEY"],
base_url="https://api.desktopaccountingapi.com/v1", # a trailing /v1 is fine
timeout=120.0, # per attempt, as in Conductor
max_retries=2,
default_headers={"X-Trace-Id": "billing-sync"},
http_client=httpx.Client(),
)
print(client.base_url)
What to change by hand:
- API key and base URL.
DAAPI_SECRET_KEY(sk_test_...,sk_live_...) instead ofCONDUCTOR_SECRET_KEY;CONDUCTOR_BASE_URLis not read. - End-user IDs. Create end users here (
eu_...); Conductor'send_usr_...IDs do not exist in this API. - Pages.
list()returns a pager: iterate it, or usefirst_page(),iter_pages()andlist_all(). Pages are plain data (data,next_cursor,has_more); replacepage.has_next_page()/page.get_next_page()loops withiter_pages(). - Timeouts.
timeoutis a number of seconds per attempt; anhttpx.Timeoutobject is not accepted. Usetotal_timeoutto cap a whole call. - Errors.
APIConnectionErroris not a subclass ofAPIErrorhere; catchConductorError(DaapiError) to cover both. Errors have noresponseorrequestattribute;headers,bodyandstatus_codeare there. - Per-call extras.
extra_headers,extra_queryandextra_bodyare not supported; usedefault_headerson the client. - Retries. Writes always carry an
Idempotency-Key, and only safe failures are retried (see Retries and idempotency); Conductor's SDK retried 408 and 409 too.
The migration guide covers the API-level differences.
Versioning and changelog
- This package follows semantic versioning. Only a major version removes or renames anything in the SDK's public API.
- The Python, Node.js, .NET and Java SDKs and the MCP server are released together with the same version number, generated from the same API contract.
- Every release is listed in CHANGELOG.md and tagged
v<version>on GitHub. - The API is versioned in its path (
/v1). Withinv1the API only adds operations, fields, enum values and error codes, and the SDK tolerates all of them, so older SDK versions keep working. - Each release is generated from one version of the API contract;
.daapi-sdk.jsonanddesktopaccountingapi.CONTRACT_SHA256record its digest (sha256:6f5ac28d7c33...for this release),desktopaccountingapi.API_VERSIONthe API version anddesktopaccountingapi.__version__the package version.
Support
- Documentation, the API reference and the error catalog.
- Status page for API and connection incidents.
- SDK bugs and feature requests: GitHub issues.
- Questions about your account, keys, billing or a specific end user's connection: contact us. Include the
request_idof a failing call, never your secret key. - Security reports: use Report a vulnerability on the repository's Security tab.
Development
The toolchain is pinned in mise.toml. mise run check installs the pinned dev tools with uv and runs ruff, mypy --strict, the unit tests, the cross-language conformance suite (against conformance/mock-server.mjs), the example type checks, the README samples (mypy --strict on every Python block of this file, and the quickstart against the mock server) and a build plus clean install of the wheel. Set UV_PYTHON=3.9 (or any supported version) to run it on another interpreter.
To install from source: pip install "git+https://github.com/DesktopAccountingAPI/quickbooks-desktop-python.git@v0.2.0". The code under src/desktopaccountingapi/types and src/desktopaccountingapi/resources, api.md, conformance/fixtures and this README are generated; see CONTRIBUTING.md.
License
MIT. See LICENSE.
QuickBooks is a registered trademark of Intuit Inc. Desktop Accounting API is an independent product and is not affiliated with, endorsed by, or approved by Intuit Inc.
Metadata
Release files for desktopaccountingapi-quickbooks-desktop 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| desktopaccountingapi_quickbooks_desktop-0.2.0.tar.gz | 505.4 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| desktopaccountingapi_quickbooks_desktop-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.1 MB
Release files / desktopaccountingapi_quickbooks_desktop-0.2.0.tar.gz
| Download URL | desktopaccountingapi_quickbooks_desktop-0.2.0.tar.gz |
|---|---|
| Size | 505.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f36ec09dc856b816ac815070952ed4dd56221af49a559d0cfc90c5ce512f4482
|
|
BLAKE2b-256 checksum How to use checksums |
8dd5d521a94b571fbd5d1c2bae05bd13ec20c7d7441de6c06e079d0e4d9f8d71
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / desktopaccountingapi_quickbooks_desktop-0.2.0-py3-none-any.whl
| Download URL | desktopaccountingapi_quickbooks_desktop-0.2.0-py3-none-any.whl |
|---|---|
| Size | 578.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7af53894807d974091f822431f183f21b3976e11dc143af197e0be4d449d83e2
|
|
BLAKE2b-256 checksum How to use checksums |
ed896ee45eb5add6e15ae551349025fae4db45d4f0fb1457b1a65b644703028c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|