Moving files from your own code: an API guide

By Hein de Wilde··4 min read

Sending files from your own code is four steps: create a transfer, upload the bytes, finalise it, and notify someone. The step that decides whether the whole thing works in production is the second one, and the property to check before you commit to a provider is whether an interrupted upload resumes or restarts.

Most consumer file transfer services have no public API at all, which is the first thing to establish. This is a guide to building the pipeline once you have found one that does.

The shape of the thing

Whatever the provider, an API-first delivery flow looks like this:

  1. Create a transfer or container. You get an id and, usually, per-file upload credentials.
  2. Upload each file's bytes. This is the part that takes hours and fails.
  3. Finalise. Tell the service the bytes are complete. This is when size accounting, virus scanning and preview generation typically start.
  4. Notify. Send the link, or hand it to whatever comes next.

The mistake worth avoiding: treating step 3 as optional because step 2 finished. Uploading the bytes and having a live transfer are different states in most designs, and stopping after the upload leaves you with something that looks sent and notifies nobody, with no error anywhere to find.

The questions to ask before choosing

Are uploads resumable? A 100 GB upload will be interrupted. If the answer is no, the advertised size limit is fiction. Why this is harder than it sounds.

Is the API on every plan, or only the expensive one? In this category it is frequently the latter.

Is there an OpenAPI document? Generated clients are only as good as the spec, and a hand-maintained spec drifts from the implementation. A spec generated from the same schemas the server validates with is worth much more than a prettier one that is wrong.

How is it billed? Per call, per gigabyte, per month? Metering on downloads is the one to watch: it means a refusal lands in front of your customer's client rather than in front of you, which is a support incident you did not budget for.

What are the rate limits, and do they fail open or closed?

Is there a service level agreement? Worth asking of anyone, and worth stating about myself: Yungle has none, because it runs on a single server with no failover. That is documented at /developers rather than softened into an uptime figure, and if your pipeline needs a contractual number, buy from someone who sells one.

A worked example

Using the Yungle API, since I built it and can show real calls. Three requests and the bytes:

# 1. Create a draft. Nothing is live and nobody is emailed yet.
curl -X POST https://yungle.co/api/v1/transfers \
  -H "Authorization: Bearer $YUNGLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"files":[{"name":"render.mov","sizeBytes":42000000000}]}'

The response carries a transfer id and, per file, a tus upload endpoint and a signed token. Then you upload with any tus client — the protocol is the resumability, and it is why it is worth using something standard rather than a bespoke multipart scheme.

# 2. Upload. Any tus client; the CLI does this for you.
npx yungle-cli upload render.mov --transfer "$TRANSFER_ID"

# 3. Finalise and send.
curl -X POST "https://yungle.co/api/v1/transfers/$TRANSFER_ID/send" \
  -H "Authorization: Bearer $YUNGLE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recipients":["client@example.com"],"message":"Final grade attached."}'

The full reference is at /developers, and the OpenAPI document at /api/v1/openapi.json is generated from the same schemas the handlers validate with — so a generated client cannot be built against a spec the server disagrees with.

Design decisions that are painful to change later

Where the retry lives. Retrying a whole upload at the job level is easy and wrong for large files. Retry at the chunk level, which means using a protocol that has chunks.

Idempotency. Network failures mean you will send the same request twice. If creating a transfer twice creates two transfers, you will find out in production. Ask what the idempotency story is.

What identifies a file. Name plus size is not unique — a folder of exports collides constantly. If you are matching created rows against local files, match by position or by an id the API gave you, never by filename.

Where credentials live. An API key is a machine credential belonging to a workspace, not to the person who created it. Treat rotation as a routine operation rather than an incident.

What happens on lapse. If billing fails, does the API stop, or do existing links keep working? Both are defensible; only one of them breaks your customer's delivery quietly.

Metering, and why it is worth reading the fine print

Providers meter uploads, downloads, calls, or storage. The choice tells you something about the product.

Metering downloads is the one to be wary of. Downloads are unbounded and unpredictable — you cannot know in advance how many times a client will fetch a file — and when the limit hits, the failure surfaces to the person you sent the link to.

Metering uploads is predictable: you know what you are about to send. It is the half a developer can plan around.

For what it is worth, Yungle meters API uploads and never downloads, with 10 GB a month included on every tier including free, then charged from prepaid credit. Plans themselves are unmetered on the byte path.

Where to go next

If you want to try the sequence above rather than read about it, the API key takes a minute and the free tier includes 10 GB of API uploads a month — enough to wire up a real pipeline and see whether the shape fits before any money changes hands.

Hein de Wilde

I build and run Yungle, and I write everything here. Comparisons name competitors and credit them, every claim about another company comes from that company’s own documentation, and where we fall short it says so. More about who is behind this.

Read next