PARTNER API · V1

API documentation

Convert PDFs and images to editable PPTX with the Partner API. Authentication, full-element conversion, uploads, polling, downloads, quotas, errors and a runnable Python example.

Use this server-to-server API to convert a PDF or slide image into a PowerPoint file. For full-element conversion, submit {"mode":"full_element_editable"}. The result contains editable text and separately selectable visual elements, usually transparent images. Native PowerPoint vectors, chart data and animations are not reconstructed automatically.

Base URL: https://2pptx.com/api/v1/partner

Contact support@2pptx.com to agree on pricing, settlement, page allowance, expected volume and support arrangements, and to obtain a dedicated API key with full-element access. Website checkout prices do not define your API agreement. This pilot API has no formal uptime or processing-time SLA.

Before you start

  • Keep the API key on your server. Send Authorization: Bearer <partner_api_key> to the API endpoints below.
  • Use the complete key saved at creation. The Admin list's key_prefix identifies a key but cannot authenticate requests; the complete key is displayed only once.
  • Both 2pptx_test_... and 2pptx_live_... keys run real production conversions and use the assigned allowance. There is no simulated/free sandbox.
  • Input: one PDF, PNG, JPG or JPEG file per job, from 1 to 36,700,160 bytes (35 MiB). PPTX input and multi-image batch upload are not supported by this API. Combine images into a PDF before uploading if you need one multi-page result.
  • Full-element access and a positive monthly full-element page allowance must be enabled for your account. The default per-job cap is 100 pages; your account may have a different cap. Images count as one page; PDF pages are counted by the server.
  • Read the data processing note. Full-element processing involves image-model providers, Canva and, when needed, authorized operators.

Quick start with the Python example

Download partner_full_element.py. It initializes a job, uploads the file, submits full-element conversion, polls status, downloads the result and checks the PPTX ZIP structure and page count.

Install its HTTP dependency:

python -m pip install httpx

Set PARTNER_API_KEY through your server's environment or secret manager. Then run:

python partner_full_element.py --file sample.pdf --idempotency-key customer-order-001 --output result.pptx

Choose a new idempotency key for each new conversion. For retries, keep the key and the file unchanged. The example also uses this value as external_job_id; do not change the filename or metadata when resuming through initialization.

The example prints partner_job_id. To resume an already submitted job without uploading again:

python partner_full_element.py --job-id pj_your_job_id --output result.pptx

Polling stops after two hours by default; this is a client timeout, not a delivery estimate or a cancellation. Use --max-wait 14400 to wait longer. API calls retry transport failures, HTTP 429 and 5xx up to five attempts, retaining the same request. If a file transfer fails, rerun the same request before expiry or resume an already submitted job by ID. The example never creates a replacement order automatically and refuses to overwrite an existing output file.

Open the downloaded PPTX to review text, layout and element separation. OCR can omit or misrecognize characters; check names, numbers and page labels against the source before using the result. A valid ZIP and matching page count do not establish visual quality.

1. Initialize an upload

POST /jobs/init-upload
Authorization: Bearer <partner_api_key>
Idempotency-Key: customer-order-001
Content-Type: application/json
{
  "file_name": "sample.pdf",
  "file_type": "pdf",
  "file_size": 123456,
  "external_job_id": "customer-order-001"
}
Field Required Meaning
file_name Yes Nonblank original filename.
file_type Yes pdf, png, jpg or jpeg.
file_size Yes Exact positive integer byte count, at most 36,700,160.
external_job_id No Your reference, returned in status responses. It does not replace Idempotency-Key.

Unknown JSON fields are rejected. Do not include mode in initialization; choose it at submission. Page quotas are checked at submission, so successful initialization does not mean you have enough quota to convert the file.

HTTP 200:

{
  "partner_job_id": "pj_example",
  "external_job_id": "customer-order-001",
  "status": "awaiting_upload",
  "upload": {
    "method": "PUT",
    "url": "https://storage.example/signed-upload-url",
    "expires_in": 1800
  }
}

Idempotency keys are scoped to your account. Repeating the same key and metadata returns the same job; changing the metadata returns 409 idempotency_conflict. The key is not a file-content checksum: never reuse it for different file contents, even if their sizes match.

Only awaiting_upload jobs receive an upload URL. A replay after submission returns identifiers and status without upload. Reinitialization does not extend the original upload deadline. An expired upload requires a new request/key; an active submitted job should be queried rather than replaced.

2. Upload the file bytes

Send the binary file to the exact upload.url, including its query string:

curl --fail-with-body --request PUT "$UPLOAD_URL" \
  --header "Content-Type: application/octet-stream" \
  --data-binary @sample.pdf

UPLOAD_URL is the URL returned by initialization. Do not send a multipart form, base64 JSON, or the Partner API Authorization header to storage. The signed URL is itself a temporary credential; keep it out of logs and public pages. Upload and download hosts may differ from the API host.

The upload URL normally lasts 30 minutes. Finish both uploading and submitting within that window. Check the upload response before submitting. The API checks the object size, then locks a copy for processing; later writes to the original URL cannot alter an accepted job.

3. Submit full-element conversion

POST /jobs/pj_example/submit
Authorization: Bearer <partner_api_key>
Content-Type: application/json
{"mode":"full_element_editable"}

HTTP 200 returns the job ID and its current status, which may already be processing:

{"partner_job_id":"pj_example","status":"queued"}

The server reserves the actual page count atomically. Insufficient allowance or an excessive page count does not create a production order. Full-element conversion uses a fixed option bundle: omit original_quality_export, ocr_textbox_merge_beta and remove_notebooklm_watermark. On the first submission, explicitly enabling the first two or disabling watermark removal returns invalid_options.

PNG and JPEG sources are decoded before reserving pages. Corrupt images or content that does not match file_type return 400 invalid_source_file without creating an order or reserving allowance. An unreadable PDF returns page_count_failed.

Repeat submission with the same effective options after a timeout or uncertain response. Both ordinary and full-element replays return the original job's status, including processing and terminal states; they do not restart it or reserve pages again. Changed options return 409 submit_options_conflict. Revoking a feature grant prevents new submissions but still allows accepted jobs to be replayed with their original options. Disabling the entire account prevents all Partner API requests; contact support in that case. An upload that expired before its first submission cannot be resumed this way.

For ordinary text-editing conversion, the optional mode defaults to auto. Its optional booleans are remove_notebooklm_watermark (default true), original_quality_export and ocr_textbox_merge_beta (both default false, subject to account permissions and feature quotas). Always send the explicit full-element mode when that is the desired output. Unknown fields, including a misspelled model, are rejected with 422 invalid_request.

4. Poll the job

GET /jobs/pj_example
Authorization: Bearer <partner_api_key>

Example during processing:

{
  "partner_job_id": "pj_example",
  "external_job_id": "customer-order-001",
  "status": "processing",
  "file_name": "sample.pdf",
  "file_type": "pdf",
  "file_size": 123456,
  "page_count": 3,
  "created_at": "2026-10-12T01:00:00+00:00",
  "submitted_at": "2026-10-12T01:01:00+00:00",
  "completed_at": null,
  "expires_at": "2026-10-13T01:01:00+00:00",
  "error": null,
  "mode": "full_element_editable",
  "phase": "preprocessing",
  "quota_status": "reserved"
}

Timestamps are ISO 8601 with a timezone offset. Poll every 3–5 seconds. Use the top-level status to decide whether the job is terminal:

Status Action
awaiting_upload Finish upload and submit within the upload deadline.
queued Wait for shared production capacity.
processing Continue polling; recovery may require operator intervention.
completed Request and save the final PPTX.
failed Stop polling and contact support with the job ID and error.
cancelled The order was terminated; it cannot be restarted by resubmitting.
expired Upload, processing or result availability has expired.

Full-element jobs expose mode, phase and quota_status after successful submission. Before that, these fields are absent and page_count is zero. Processing phases are:

Phase Meaning
waiting_capacity Waiting for admission; top-level status is queued.
preprocessing Automatic image and text preprocessing.
preprocess_failed Preprocessing needs operator recovery.
awaiting_manual Waiting for a Runner or operator.
manual_processing Runner/operator is producing the result.
manual_recovery / manual_failed Production needs recovery.
manual_review Awaiting review.
completed Final PPTX delivered.
failed / expired Processing terminated or expired.

Recovery phases keep status=processing and the reservation. Do not create a replacement job just because a phase contains failed. Contact support if progress stalls. Intermediate preprocessing files are not delivered through the Partner download endpoint.

expires_at describes the current processing deadline before delivery. On delivery it becomes the result-access deadline, normally 24 hours after completion. It is not an estimated completion time; always use the value returned by the API.

5. Download the result

GET /jobs/pj_example/download
Authorization: Bearer <partner_api_key>

HTTP 200:

{"download_url":"https://storage.example/signed-download-url","expires_in":3600}

Then download the actual file, without the Partner Authorization header:

curl --fail-with-body "$DOWNLOAD_URL" --output result.pptx

This endpoint returns JSON, not PPTX bytes. A signed link normally lasts one hour. If it expires while the result is still available, request a fresh link for the same job. Save your own copy promptly; the download endpoint stops issuing links once the job expires. Result access expiry is separate from physical storage deletion and third-party retention; see the data processing note.

Allowance, queueing and billing

  • Full-element allowance is separate from ordinary and enhancement quotas. Exhausted ordinary quota does not block full-element uploads or submissions.
  • Submission sets quota_status=reserved. Final delivery changes it to settled. Termination or processing expiry releases an unsettled reservation (released). Result download expiry does not refund delivered pages.
  • Replays and replacement delivery do not bill pages twice. Undelivered reservations carry across month boundaries. Settled pages count in the delivery month, using the application's calendar month; the default timezone is Asia/Shanghai (UTC+08:00), not a rolling 30-day window.
  • Full-element allowance is finite: zero disallows new full-element submissions. Ordinary quota may be unlimited when unset; zero disallows ordinary submissions.
  • Ordinary, HD and textbox-merge usage counts delivered pages in the delivery month. Result download expiry does not remove these pages from quotas or usage reports. Jobs that fail, are cancelled or expire before delivery do not consume completed-page allowance.
  • Default limits are 10 job creations and 10 submissions per minute per account, 20 queued jobs per account, one active full-element job per account and one active full-element job across all Partners. Account limits may differ, but increasing an account limit does not override the global limit or shared website capacity.
  • Accepted jobs can wait in the shared production queue. API submission does not open a payment checkout. API pricing, payment and reconciliation follow your agreed account terms.

There are currently no public Partner endpoints for account balance/usage, job listing, cancellation, or webhooks. Store the returned job IDs, poll for updates, and ask support for usage, allowance changes or termination. Send your partner_job_id and external_job_id, never your API key.

Errors and retries

Application-generated Partner API errors use this envelope:

{"error":{"code":"full_element_quota_exceeded","message":"Full-element monthly quota is insufficient, including reserved pages."}}

Validation errors use HTTP 422 and include error.details, a list of field, message and type entries. Parse code, not English message text. Storage services and reverse proxies can return a different body, including XML or HTML; handle non-JSON HTTP failures as well.

HTTP Code Response
400 idempotency_key_required Supply a stable Idempotency-Key for initialization.
400 unsupported_file_type, file_too_large Correct the file type or size.
400 invalid_options, page_count_failed Correct the options or provide a readable, unencrypted PDF.
400 invalid_source_file Provide a readable PNG or JPEG whose content matches file_type; no pages were reserved.
400/403 option_not_enabled Use a supported mode/feature and obtain permission.
401 invalid_api_key Check the key and exact Bearer header; do not keep retrying.
403 partner_disabled Contact support.
403 full_element_page_limit_exceeded Reduce page count or agree on a higher per-job cap.
403 full_element_quota_exceeded Wait for reserved pages to be released or request more allowance.
403 quota_exceeded, original_quality_quota_exceeded, textbox_merge_quota_exceeded Ordinary/feature quota is exhausted.
404 job_not_found Check the ID and account; other accounts' jobs are not accessible.
404/405 not_found, method_not_allowed Correct the URL or HTTP method.
409 idempotency_conflict, submit_options_conflict Restore the original request; do not silently change an accepted job.
409 upload_not_found Ensure PUT succeeded and the byte count matches initialization; retry the same job while its upload window is open.
409 upload_expired Create a new upload with a new key only after confirming the old upload expired.
409 job_not_completed Continue polling before requesting a download.
409/410 job_expired Job cannot be submitted/downloaded in its current state; query it before deciding on a replacement.
422 invalid_request Correct required fields, values, JSON syntax or unknown fields.
429 rate_limited, too_many_queued_jobs Respect Retry-After (normally 60 seconds), then retry the same request.
503 storage_unavailable Respect Retry-After (normally 5 seconds), then retry the same request.
500 internal_error Retry with bounded backoff and the same identifiers; contact support if persistent.
Other 4xx http_error Correct the request as described by the message.

For transport timeouts or proxy 502/504 responses, the outcome may be uncertain. Retry initialization with the same key and metadata, or query/re-submit the same job with the same full-element options. Never generate a new key just because a response was lost. Use bounded retries with increasing delays; production clients should add jitter. Retry a failed binary download by obtaining a fresh signed URL while the result is still available.