This API allows you to convert Docsend documents to downloadable PDFs programmatically.
POST https://docsend2pdf.com/api/convertContent-Type: application/json{
"url": "https://docsend.com/view/abcdefg", // Required
"email": "user@example.com", // Optional, for email-gated documents
"passcode": "document-password", // Optional, for password-protected documents
"async": true, // Optional, default true; false restores blocking mode
"searchable": false // Deprecated - always false
}The API is asynchronous by default. Small documents that finish immediately return the PDF directly; otherwise you receive a task to poll:
200 with Content-Type: application/pdf - the conversion finished immediately; the body is the PDF (with Content-Disposition: attachment; filename=*.pdf)202 with JSON - the conversion is queued:{
"task_id": "1c0e4f6a-...",
"status": "queued",
"poll_url": "/api/task/1c0e4f6a-..."
}Poll GET https://docsend2pdf.com/api/task/{task_id} every 1-2 seconds:
200 JSON with status: "queued" or "processing" - keep polling200 with Content-Type: application/pdf - done; the result stays available for about 5 minutes after completion, so retried downloads are safe200 JSON with status: "email_verification_required" - see below400 JSON - the conversion failed (see error format below)404 - unknown or expired taskBlocking mode: send "async": false and the request stays open until the PDF or an error is returned (up to 5 minutes).
The API also includes rate limiting headers:
X-RateLimit-Limit: Maximum requests allowed in the time windowX-RateLimit-Remaining: Remaining requests in the current time windowX-RateLimit-Reset: Timestamp when the rate limit window resetsEmail-gated documents: if the document requires email verification, the task status (or, in blocking mode, the response itself) reports it as JSON instead of a PDF:
{
"status": "email_verification_required",
"session_id": "...",
"message": "Please check your email to verify access",
"details": { }
}Email verification can only be completed interactively on the website, so always check the Content-Type response header before treating a 2xx response as a PDF.
Errors are returned as JSON with appropriate HTTP status codes:
{
"error": "Technical error message",
"user_message": "Human-friendly message suitable for display",
"details": { },
"errorCode": 400
}user_message is always safe to show to end users; error may contain technical detail intended for logs.
Common error status codes:
400 - Bad Request (missing or invalid parameters, or the document could not be accessed)404 - Not Found (conversion task expired or was already collected)429 - Too Many Requests (rate limit exceeded)500 - Internal Server Error504 - Gateway Timeout (conversion exceeded the 5-minute limit)To ensure fair usage and service stability, the following rate limits apply:
If you exceed this limit, requests will be rejected with a 429 status code and include a Retry-After header indicating when you can retry.
# Start a conversion (async by default)
curl -s -X POST https://docsend2pdf.com/api/convert \
-H "Content-Type: application/json" \
-d '{"url": "https://docsend.com/view/abcdefg"}'
# => {"task_id": "...", "status": "queued", "poll_url": "/api/task/..."}
# Poll every second or two until the response is the PDF itself
curl -sL --output document.pdf https://docsend2pdf.com/api/task/TASK_ID
# Blocking mode - one request that waits for the PDF (up to 5 minutes)
curl -L -X POST https://docsend2pdf.com/api/convert \
-H "Content-Type: application/json" \
-d '{
"url": "https://docsend.com/view/abcdefg",
"passcode": "document-password",
"async": false
}' \
--output document.pdf// Using fetch (browser or Node.js 18+)
async function convertDocsend(url) {
const start = await fetch('https://docsend2pdf.com/api/convert', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ url })
});
if (!start.ok) {
if (start.status === 429) {
const retryAfter = start.headers.get('Retry-After');
console.error(`Rate limit exceeded. Retry after ${retryAfter} seconds`);
return null;
}
const errorData = await start.json();
console.error('API Error:', errorData.user_message || errorData.error);
return null;
}
// Small documents can finish immediately
if ((start.headers.get('content-type') || '').includes('application/pdf')) {
return await start.blob();
}
const { task_id } = await start.json();
const deadline = Date.now() + 5 * 60 * 1000;
while (Date.now() < deadline) {
await new Promise((resolve) => setTimeout(resolve, 1000));
const poll = await fetch(`https://docsend2pdf.com/api/task/${task_id}`);
if (poll.ok && (poll.headers.get('content-type') || '').includes('application/pdf')) {
return await poll.blob();
}
const data = await poll.json();
if (!poll.ok || data.status === 'failed') {
console.error('API Error:', data.user_message || data.error);
return null;
}
if (data.status === 'email_verification_required') {
console.error('Email verification required - convert on docsend2pdf.com');
return null;
}
// queued / processing - keep polling
}
console.error('Conversion timed out');
return null;
}
// Example usage
const pdfBlob = await convertDocsend('https://docsend.com/view/abcdefg');
if (pdfBlob) {
// Do something with the PDF blob
// e.g., save to file, display in browser, etc.
}import time
import requests
def convert_docsend(url, email=None, passcode=None):
"""
Convert a Docsend document to PDF, polling until it completes.
Args:
url (str): The DocSend URL
email (str, optional): Email for email-gated documents
passcode (str, optional): Password for password-protected documents
Returns:
bytes: The PDF file content or None if conversion failed
"""
api = 'https://docsend2pdf.com'
payload = {'url': url}
if email:
payload['email'] = email
if passcode:
payload['passcode'] = passcode
start = requests.post(f'{api}/api/convert', json=payload, timeout=30)
if not start.ok:
if start.status_code == 429:
retry_after = start.headers.get('Retry-After')
print(f"Rate limit exceeded. Retry after {retry_after} seconds")
return None
try:
error_data = start.json()
message = error_data.get('user_message') or error_data.get('error', 'Unknown error')
print(f"API Error: {message}")
except ValueError:
print(f"API Error: {start.status_code} - {start.text}")
return None
# Small documents can finish immediately
if 'application/pdf' in start.headers.get('Content-Type', ''):
return start.content
task_id = start.json()['task_id']
deadline = time.time() + 300
while time.time() < deadline:
time.sleep(1)
poll = requests.get(f'{api}/api/task/{task_id}', timeout=30)
if poll.ok and 'application/pdf' in poll.headers.get('Content-Type', ''):
return poll.content
data = poll.json()
if not poll.ok or data.get('status') == 'failed':
message = data.get('user_message') or data.get('error', 'Unknown error')
print(f"API Error: {message}")
return None
if data.get('status') == 'email_verification_required':
print('Email verification required - convert on docsend2pdf.com')
return None
# queued / processing - keep polling
print('Conversion timed out')
return None
# Example usage
pdf_data = convert_docsend('https://docsend.com/view/abcdefg')
if pdf_data:
# Save the PDF to a file
with open('document.pdf', 'wb') as f:
f.write(pdf_data)