Web and network - transfer.sh

Free transfer.sh Alternative

transfer.sh taught a generation of shell users to share a file with one curl command and get a link back. On 4 November 2026 its public host refused our connections, and its README tells users to host their own copy. file.io, the other name people reach for, has retired its command-line API and takes uploads in the browser only. The Storage API endpoint keeps the habit: one curl command uploads a file, JSON or text, the answer is a link with a SHA-256 of what was stored, the link works for 24 hours or for exactly the number of downloads you choose, and no account is involved. This page sets the commands side by side.

The translation

The transfer.sh facts are from its README as read on 4 November 2026, the file.io facts from its own pages the same day.

Steptransfer.shaisenseapi.com
Upload a filecurl --upload-file f https://transfer.sh/fcurl -F file=@f to /services/v1/storage
Upload text or JSONThe same, as a fileThe raw body with Content-Type: text/plain or application/json, stored verbatim
The answerThe download URL as textJSON: storage_id, storage_url, sha256_hash, bytes and the expiry
Limit the downloads-H "Max-Downloads: 1"/storage/max_downloads/1 in the path, 1 to 100; the answer adds downloads_left
Set the lifetime-H "Max-Days: 1"Fixed: 24 hours from upload, or the last allowed download, whichever comes first
Downloadcurl https://transfer.sh/...curl -o f "$storage_url"; each download reports Downloads-Left
Delete earlyAn X-Url-Delete URL in the upload responseNot offered; choose the download count instead
IntegrityNot statedsha256_hash of the stored bytes in the upload answer
SizeNot stated for the public host80 MB per address per day across all uploads; JSON bodies at most 256 KiB
Account or keyNoneNone
StatusPublic host unreachable when read; the README says host your ownHosted, HTTPS only
file.io, for comparisonBrowser uploads only; its API is retired. Free files up to 4 GB, kept 3 days, up to 10 downloads per file per day, no account.

The upload and its answer:

curl -s -F file=@report.pdf https://aisenseapi.com/services/v1/storage/max_downloads/1
{
  "storage_id": "5e1c0a7e-9b3f-4c62-8a1d-2f7b6d4c9e10",
  "storage_url": "https://aisenseapi.com/services/v1/storage/5e1c0a7e-9b3f-4c62-8a1d-2f7b6d4c9e10",
  "sha256_hash": "700d8cea137c32c31e4865aaf5ae327931067ae454bc78777f8572dadf9c9809",
  "bytes": 48213,
  "expire_timestamp": 1793881430,
  "expire_datetime": "2026-11-05T12:23:50+00:00",
  "downloads_max": 1,
  "downloads_left": 1
}

What differs

  • Time-boxed by design. transfer.sh let the uploader pick days. Here every object lives at most 24 hours; with max_downloads it can live shorter, never longer. This is a hand-off, not a place to keep files.
  • A link that works exactly once. max_downloads/1 makes a link whose first fetch is the only fetch; the object answers 410 afterwards. It is the right shape for passing a file to one recipient or one machine, with the SHA-256 to prove what arrived.
  • A daily budget rather than a size cap. An address may store 80 MB per day in total; past that an upload answers 429 until the counter resets at midnight in Norway. That fits logs, exports, images and documents, and does not fit video.
  • Executables are refused. A body whose first bytes are a Windows, Linux or macOS executable, or a Java class, is refused whatever its name or declared type says.
  • JSON answers. transfer.sh answered a URL as text. Here the upload answers JSON with the id, the URL, the hash, the size and the expiry, so a script checks the hash and keeps the URL without parsing text.
  • No custom names. transfer.sh kept the file name in the link. Here the link is a UUID; keep the name on your side, or in the hand-off message.

A hand-off from a shell and from Python

Upload once, pass the URL on, and let the single download remove the object:

answer=$(curl -s -F file=@build.log https://aisenseapi.com/services/v1/storage/max_downloads/1)
url=$(printf '%s' "$answer" | jq -r .storage_url)
sha=$(printf '%s' "$answer" | jq -r .sha256_hash)
echo "Fetch once: $url (sha256 $sha)"

# the recipient
curl -s -D - -o build.log "$url" | grep -i downloads-left   # Downloads-Left: 0
sha256sum build.log

The same in Python, with the hash check on the receiving side:

import hashlib, requests

BASE = "https://aisenseapi.com/services/v1/storage"

with open("build.log", "rb") as handle:
    stored = requests.post(BASE + "/max_downloads/1", files={"file": handle}, timeout=60).json()
print(stored["storage_url"], stored["bytes"], stored["sha256_hash"])

got = requests.get(stored["storage_url"], timeout=60)
assert hashlib.sha256(got.content).hexdigest() == stored["sha256_hash"]
print("downloads left:", got.headers.get("Downloads-Left"))

Text and JSON need no form: send the body with its content type and it is stored byte for byte.

curl -s -X POST https://aisenseapi.com/services/v1/storage \
  -H "Content-Type: text/plain" \
  --data-binary @notes.txt

The fields

FieldTypeMeaning
storage_idstringThe UUID of the object; the only thing that reads it.
storage_urlstringThe GET URL that returns the stored bytes.
sha256_hashstringSHA-256 of the bytes as stored, so the recipient can check what arrived.
bytesintegerThe stored length.
expire_timestamp, expire_datetimeinteger, stringWhen the object is removed, 24 hours after upload.
downloads_max, downloads_leftintegerPresent when a download limit was set; each download also answers a Downloads-Left header.

Errors

CaseStatusWhat it means
Unknown id404No object has that id.
Expired, or the last allowed download has gone out410The bytes are removed; the id answers 410 for the rest of its 24 hours.
The day's storage budget or request quota used up42980 MB stored per address per day, or 5,000 requests; wait for the reset.

Every refusal is JSON with error and fix.

Privacy and limits

Whoever holds the storage_url can fetch the bytes until they expire or the downloads run out, so treat it as a capability link and send it only to the intended recipient. Do not store secrets, credentials or personal data here; the One-time Secret endpoint exists for a secret that must be read once. See our privacy policy for log retention.

Every path shares the service-wide ceiling of 5,000 requests per IP address per day, and uploads share the 80 MB per address per day. transfer.sh's README documents Max-Downloads, Max-Days and a delete URL and states no size or retention for the public host; file.io states 4 GB, 3 days and 10 downloads per file per day for a free upload in the browser. Those are their statements as of November 2026 and may change.