Python · API

Image hosting API Python, one function.

A copy-paste function that uploads a local image from Python and returns the CDN URL. Uses the requests library (pip install requests).

Python
import requests

def upload_image(path: str, api_key: str = None) -> str:
    """Upload a local image, return the hosted CDN URL."""
    headers = {}
    if api_key:
        headers["Authorization"] = f"Bearer {api_key}"

    with open(path, "rb") as f:
        r = requests.post(
            "https://imagetourl.cloud/api/upload",
            headers=headers,
            files={"file": f},
            timeout=30,
        )
    r.raise_for_status()
    body = r.json()
    if body.get("error"):
        raise RuntimeError(body["error"])
    return body["data"]["url"]

# Example
url = upload_image("./screenshot.png", api_key="YOUR_KEY")
print(url)  # https://imagetourl.cloud/uploads/abc123.png

Why use ImageToURL's API

Works with any Python

3.8+. No async required, no third-party SDK. The requests library is already on most systems.

Async variant with httpx

Swap requests for httpx.AsyncClient for async FastAPI / Django services — same multipart pattern.

Drop into scripts or services

Data pipelines, Jupyter notebooks, Airflow DAGs, Celery tasks — anywhere you handle image bytes.

Integrates with Pillow

Pillow.Image → BytesIO → upload works in one chain. Pre-process (resize, compress) in-memory, then POST the final bytes.

FAQ

Should I use async?

For a single upload in a script: no. For a web service uploading on every request: yes — swap requests for httpx.AsyncClient to avoid blocking. The POST call is otherwise identical.

Can I upload from a BytesIO buffer instead of a file path?

Yes. Pass files={'file': ('name.png', buffer, 'image/png')} — requests handles the multipart boundary either way.

Error handling?

r.raise_for_status() raises on HTTP errors. Check body['error'] for API-level errors (e.g. quota exceeded, invalid file type). Wrap in try/except for network issues.

How do I upload from a Pillow image?

img = Image.open('...'); buf = BytesIO(); img.save(buf, format='PNG'); buf.seek(0); then pass buf to the files parameter.

Rate limits from Python?

Same as any client: 10/min anonymous, 60/min Pro, 600/min Business. Add backoff with tenacity or retry for 429 responses.

Does it work in Jupyter / Colab?

Yes. requests works identically in notebook contexts. Output the URL with display(Image(url=...)) to preview inline.

Parallelism — is it safe?

Yes. Use concurrent.futures.ThreadPoolExecutor for bulk uploads. Tune workers to match your rate-limit tier.

Django ImageField integration?

Override storage to upload to ImageToURL on save, store the returned URL in a URLField instead of a local ImageField. Pattern documented in our docs.