Skip to main content
Text promotions skip this step entirely. If your content is plain text or HTML, provide it inline via the content field when creating a promotion.
Uploads use a resumable chunked protocol. You initiate the upload, then send the file in one or more chunks using Content-Range headers.
1

Initiate the upload

Request:
POST https://files.adclear.ai/v1/uploads

{
  "fileName": "summer-campaign-v1.pdf",
  "fileSize": 2048576,
  "contentType": "application/pdf"
}
Response:
{
  "uploadId": "a1b2c3d4-...",
  "uploadUrl": "/v1/uploads/a1b2c3d4-...",
  "maxChunkSize": 52428800
}
  • uploadUrl is the relative path for chunk uploads.
  • maxChunkSize is the maximum bytes per chunk (default 50 MB). The final chunk may be smaller.
2

Upload the file in chunks

Send the file bytes as one or more PUT requests with a Content-Range header.
PUT https://files.adclear.ai/v1/uploads/{uploadId}
Content-Type: application/pdf
Content-Range: bytes 0-2048575/2048576

<binary file data>
Content-Range format: bytes <start>-<end>/<total>, where start and end are zero-based inclusive byte offsets and total is the full file size. For a multi-chunk upload, repeat with the next byte range until the response status is completed.
3

Check upload status (optional)

You can poll upload status at any time:
GET https://files.adclear.ai/v1/uploads/{uploadId}
{
  "uploadId": "a1b2c3d4-...",
  "status": "completed",
  "fileName": "summer-campaign-v1.pdf",
  "fileSize": 2048576,
  "createdAt": "2026-02-16T12:00:00.000Z"
}
Status transitions: pendinguploadingcompleted | failed.

Full upload example

The curl tab covers the single-chunk happy path. The TypeScript and Python tabs handle chunking for large files.
# Initiate
UPLOAD=$(curl -s -X POST "$FILES_BASE/v1/uploads" \
  -H "Authorization: Bearer $ADCLEAR_API_KEY" \
  -H "X-External-User-ID: $USER_ID" \
  -H "Content-Type: application/json" \
  -d '{ "fileName": "ad.pdf", "fileSize": 2048576, "contentType": "application/pdf" }')
UPLOAD_ID=$(echo "$UPLOAD" | jq -r .uploadId)

# Send the bytes (single chunk)
curl -s -X PUT "$FILES_BASE/v1/uploads/$UPLOAD_ID" \
  -H "Authorization: Bearer $ADCLEAR_API_KEY" \
  -H "Content-Type: application/pdf" \
  -H "Content-Range: bytes 0-2048575/2048576" \
  --data-binary @ad.pdf

Implementation notes

  • A promotion references exactly one upload. To review several files, create a separate promotion for each.
  • If a chunk fails due to a network error, check the upload status to see how many bytes were received, then resume from that offset.
  • Uploading to an already-completed upload returns 409 Conflict.

Common error scenarios

ScenarioHTTP statusError codeWhat to do
File too large for its format413PAYLOAD_TOO_LARGECheck the size limits in Introduction
Missing Content-Range header400VALIDATION_ERRORAdd Content-Range: bytes <start>-<end>/<total>
Chunk body size doesn’t match the range span400VALIDATION_ERROREnsure the body’s byte length equals end - start + 1
Content-Range total doesn’t match declared fileSize400VALIDATION_ERRORUse the same total in every chunk as the fileSize from initiation
Upload already completed409INVALID_REQUESTThe file is already uploaded; proceed to create the promotion
Upload not found404NOT_FOUNDThe upload ID is invalid or the session expired; initiate a new upload
Network failure mid-uploadN/AN/AGET the upload status to check bytesReceived, then resume from that offset
For cross-cutting errors (401, 403, 429, 502), see Errors & rate limits.

Frequently asked questions

No. A promotion references exactly one upload. To review several files, create a separate promotion for each.
Yes. Upload IDs aren’t consumed. The same upload can be referenced by multiple promotions or versions.
Completed uploads are stored indefinitely. If you initiate an upload and never send any chunks, the upload session may eventually become unusable, so complete uploads promptly after initiation.