ContainerDL API Documentation

Programmatic access to export Docker and Kubernetes images.

Base URL https://containerdl.com

Quick Start

  1. Generate an API key in your account (Light/Pro).
  2. Authenticate requests with:
    Bearer token Authorization: Bearer <key>
  3. 1 credit = 1 image export.

Authentication

API Key

Use a personal key for scripts and CI/CD.

Available on Light and Pro plans.

Sign in to manage your API key.

Bearer token Authorization: Bearer cdl_live_xxx

Endpoints

POST /v1/exports

Export a public container image to tar.

{
  "image": "nginx:latest"
}
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "queued",
  "image": "nginx:latest",
  "createdAt": "2026-01-04T20:00:00Z",
  "sizeBytes": null
}
curl -X POST https://containerdl.com/v1/exports \\
  -H "Content-Type: application/json" \\
  -H "Authorization: Bearer cdl_live_xxx" \\
  -d '{"image":"nginx:latest"}'
POST /v1/exports

Export from a private GHCR or Docker Hub image.

Credentials are used only to pull the image for this request, stored temporarily in memory during processing, and never stored in database or logs.

{
  "image": "ghcr.io/myorg/private-app:v1.0",
  "registry_auth": {
    "username": "myusername",
    "password": "ghp_myPersonalAccessToken"
  }
}
curl -X POST https://containerdl.com/v1/exports \\
  -H "Content-Type: application/json" \\
  -H "Authorization: Bearer cdl_live_xxx" \\
  -d '{
    "image":"ghcr.io/myorg/private-app:v1.0",
    "registry_auth":{
      "username":"myusername",
      "password":"ghp_myPersonalAccessToken"
    }
  }'

Private registry authentication is available on Light and Pro plans only. Free plan users can export public images without authentication.

Supported registries: ghcr.io (PAT as password), docker.io (access token).
  • Use HTTPS only.
  • Use read-only tokens with minimal scopes.
  • Rotate tokens regularly.
  • Never commit tokens to git.
GET /v1/exports/{id}

Check export job status.

curl https://containerdl.com/v1/exports/{id} \\
  -H "Authorization: Bearer cdl_live_xxx"
GET /v1/exports/{id}/download

Download completed tar file.

curl -L https://containerdl.com/v1/exports/{id}/download \\
  -H "Authorization: Bearer cdl_live_xxx" \\
  -o myimage.tar
POST /v1/bundles

Create a multi-image bundle from a stack file.

{
  "input": "version: '3'\\nservices:\\n  web:\\n    image: nginx:latest",
  "kind": "compose"
}
curl -X POST https://containerdl.com/v1/bundles \\
  -H "Content-Type: application/json" \\
  -H "Authorization: Bearer cdl_live_xxx" \\
  -d @stack.json
GET /v1/credits

Check your current credits.

curl https://containerdl.com/v1/credits \\
  -H "Authorization: Bearer cdl_live_xxx"

Rate Limits

Error Codes

400
Bad RequestInvalid JSON or missing fields
401
UnauthorizedNot authenticated
402
Payment RequiredInsufficient credits
404
Not FoundExport ID does not exist
429
Too Many RequestsRate limit exceeded
500
Server Error

Code Examples

Copy-friendly snippets. Replace the API key and image name to get started.

Python

requests

Install dependency: pip install requests

  • Prefer a read-only key for CI.
  • Poll with a short delay (1-2s).
import requests
import time

# Export public image
headers = {"Authorization": "Bearer cdl_live_xxx"}
resp = requests.post(
    "https://containerdl.com/v1/exports",
    json={"image": "nginx:latest"},
    headers=headers
)
export_id = resp.json()["id"]

# Export private image from GHCR
resp = requests.post(
    "https://containerdl.com/v1/exports",
    json={
        "image": "ghcr.io/org/app:v1",
        "registry_auth": {
            "username": "token",
            "password": "ghp_xxxxx"
        }
    },
    headers=headers
)

# Poll status
while True:
    status = requests.get(
        f"https://containerdl.com/v1/exports/{export_id}",
        headers=headers
    ).json()
    if status["status"] == "completed":
        break
    time.sleep(2)

# Download
tar = requests.get(
    f"https://containerdl.com/v1/exports/{export_id}/download",
    headers=headers
)
with open("image.tar", "wb") as f:
    f.write(tar.content)

Bash

curl + jq

Requires jq for JSON parsing.

  • Use single quotes around JSON strings.
  • Use HTTPS and minimal token scopes.
#!/bin/bash
# Export and download
EXPORT_ID=$(curl -s -X POST \\
  https://containerdl.com/v1/exports \\
  -H "Content-Type: application/json" \\
  -H "Authorization: Bearer cdl_live_xxx" \\
  -d '{"image":"nginx:latest"}' | jq -r '.id')

# Wait for completion
while true; do
  STATUS=$(curl -s https://containerdl.com/v1/exports/$EXPORT_ID \\
    -H "Authorization: Bearer cdl_live_xxx" | jq -r '.status')
  [ "$STATUS" = "completed" ] && break
  sleep 2
done

# Download
curl -L https://containerdl.com/v1/exports/$EXPORT_ID/download \\
  -H "Authorization: Bearer cdl_live_xxx" -o image.tar

Go

net/http

Remember to handle errors and timeouts.

  • Use a client with timeouts in production.
  • Store keys outside of source control.
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
)

type ExportRequest struct {
    Image        string        `json:"image"`
    RegistryAuth *RegistryAuth `json:"registry_auth,omitempty"`
}

type RegistryAuth struct {
    Username string `json:"username"`
    Password string `json:"password"`
}

func exportPrivateImage() error {
    req := ExportRequest{
        Image: "ghcr.io/org/private:latest",
        RegistryAuth: &RegistryAuth{
            Username: "token",
            Password: "ghp_xxxxx",
        },
    }
    body, _ := json.Marshal(req)

    reqHTTP, err := http.NewRequest("POST", "https://containerdl.com/v1/exports", bytes.NewReader(body))
    if err != nil {
        return err
    }
    reqHTTP.Header.Set("Content-Type", "application/json")
    reqHTTP.Header.Set("Authorization", "Bearer cdl_live_xxx")
    resp, err := http.DefaultClient.Do(reqHTTP)
    // ... handle response
    return err
}