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).
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.