InfiniteOceanv2

Files and blobs

Upload a file up to 4 MiB with a single authenticated PUT, read it back by id, serve it publicly at a keyless URL, and delete it.

Blobs are for bytes that do not belong in a drop payload: images, small PDFs, CSV exports, icons, JSON bundles. You upload a file with one authenticated PUT, the server gives it an id, and you read it back by that id. There is no separate upload-URL step and no signed link to fetch first — the door is the upload endpoint.

One PUT is capped at 4 MiB (4,194,304 bytes). That is a hard server limit and it is the first thing to check against your use case — see Size limit. There is no chunked or multipart upload API to work around it.

Everything on this page was run against https://api2.infiniteocean.io and the responses are the real ones. HTTP header dumps are trimmed to the headers that matter; the live responses also carry date, server, alt-svc and Cloudflare headers.

Quick start

Upload a file, then read it back.

$ printf 'hello from the v2 blob door\n' > hello.txt

$ curl -s -X PUT https://api2.infiniteocean.io/v2/blob/_docs_probe/files \
    -H "X-Ocean-Key: $OCEAN_KEY" \
    -H "Content-Type: text/plain" \
    --data-binary @hello.txt

{"url":"/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183","key":"b/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183","id":"5fb4d226afcf4e70dbaeb37872567183","bytes":28,"sha256":"65b165d16026b56c86d6bd60688bc33ce87ad256ef4b10a687357091aa7833a1"}
$ curl -s https://api2.infiniteocean.io/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183 \
    -H "X-Ocean-Key: $OCEAN_KEY"

hello from the v2 blob door

The id you get back will not be the one above. The server mints a random 32-hex id per upload; it is not derived from the content. Uploading the same 28 bytes a second time produced a different id and a second, independent blob. Store the id the response gives you.

The sha256 in the response is computed by the server from the bytes it stored. Compare it with your local hash and you have proof the upload is byte-identical:

$ shasum -a 256 hello.txt
65b165d16026b56c86d6bd60688bc33ce87ad256ef4b10a687357091aa7833a1  hello.txt

Uploading

PUT /v2/blob/{route}

Stores the request body as a new blob under {route} and returns its id. Requires a key that is a principal on the route.

PartRequiredNotes
{route} in the pathyesMay contain slashes: /v2/blob/media/2026/covers is the route media/2026/covers. A single segment is also a route: PUT /v2/blob/mystuff uploads to route mystuff.
X-Ocean-Key headeryes401 without it.
Request bodyyesThe raw file bytes, with a Content-Length. Not a JSON wrapper, not multipart, not chunked. An empty body is a 400.
Content-Type headernoStored and replayed on read. Defaults to application/octet-stream.
?id= or X-Blob-IdnoName the blob yourself instead of letting the server mint an id. The header wins if both are sent (verified).
?public=truenoAlso publish the blob at the keyless gateway. See Public blobs.

Those two query parameters are the only ones this endpoint accepts. Every other blob endpoint accepts none. Anything else is a hard 400 — see Unknown query parameters.

Response is 201 with:

FieldMeaning
id32 lowercase hex characters, minted at random by the server unless you supplied one. This is what you store and what you read back with.
urlA path, not an absolute URL: /v2/blob/{route}/{id}. Prefix it with https://api2.infiniteocean.io yourself.
keyThe internal object key, b/{route}/{id}. Informational; no endpoint takes it.
bytesSize of what was stored.
sha256SHA-256 of the stored bytes, lowercase hex.

Uploading from a file with -T works the same way (curl sets Content-Length from the file):

$ curl -s -X PUT https://api2.infiniteocean.io/v2/blob/_docs_probe/files \
    -H "X-Ocean-Key: $OCEAN_KEY" \
    -H "Content-Type: application/octet-stream" \
    -T big.bin

{"url":"/v2/blob/_docs_probe/files/dceafd8831cdc4321e8155ac526dfbab","key":"b/_docs_probe/files/dceafd8831cdc4321e8155ac526dfbab","id":"dceafd8831cdc4321e8155ac526dfbab","bytes":1048576,"sha256":"f851956454a36d49a74d2214968aaba18b919bb30ada73d515b127dcd0bd630b"}

Size limit: 4 MiB per PUT

A request body larger than 4,194,304 bytes is rejected before it reaches the blob code. The response is 413 and it names the limit:

$ curl -s -X PUT https://api2.infiniteocean.io/v2/blob/_docs_probe/files \
    -H "X-Ocean-Key: $OCEAN_KEY" -H "Content-Type: application/octet-stream" -T five-mib.bin

{"ok":false,"error":"body of 5242880 bytes exceeds the 4194304-byte limit — nothing was applied","code":"PAYLOAD_TOO_LARGE","limit_bytes":4194304}

The boundary is exact and was probed on both sides: a body of exactly 4,194,304 bytes returned 201; 4,194,305 bytes returned 413 PAYLOAD_TOO_LARGE. Nothing is stored on a 413.

There is no chunked upload, no multipart upload and no resume, so 4 MiB is the largest single object you can put in a blob today. If you need to store something bigger, split it yourself into blobs under 4 MiB and record the parts in a drop you write.

Send Content-Length — chunked bodies are corrupted

Transfer-Encoding: chunked is not supported, and it fails silently rather than with an error. Two observed outcomes, both wrong:

Always send a real Content-Length. curl --data-binary @file and curl -T file both do; a streaming HTTP client that defaults to chunked does not.

Do not send multipart/form-data

The body is stored verbatim. If you send curl -F 'file=@hello.txt', the MIME envelope is what gets stored, and reading it back gives you the envelope, not the file:

$ curl -s https://api2.infiniteocean.io/v2/blob/_docs_probe/files/99999999888877776666555544443333 \
    -H "X-Ocean-Key: $OCEAN_KEY"

--------------------------xFyhkdhtToXRt0vJHovGVJ
Content-Disposition: form-data; name="file"; filename="hello.txt"
Content-Type: text/plain

hello from the v2 blob door

--------------------------xFyhkdhtToXRt0vJHovGVJ--

(The boundary string is generated by curl and differs every run.) The upload also returned 201 with "bytes":227 and a Content-Type of multipart/form-data, so nothing tells you it went wrong except reading it back. Use --data-binary @file or -T file. If you are uploading from a browser form, read the File object and send it as the request body directly.

Naming the blob yourself

Pass ?id= (or the X-Blob-Id header) when you already have an identifier — migrating from another store, or making a re-run idempotent. Two id shapes are accepted: 32 lowercase hex characters, or a lowercase dashed UUID (8-4-4-4-12).

$ curl -s -X PUT "https://api2.infiniteocean.io/v2/blob/_docs_probe/files?id=7fe20831-e59b-4878-82c5-53e47f13b524" \
    -H "X-Ocean-Key: $OCEAN_KEY" -H "Content-Type: text/plain" \
    --data-binary 'legacy uuid'

{"url":"/v2/blob/_docs_probe/files/7fe20831-e59b-4878-82c5-53e47f13b524","key":"b/_docs_probe/files/7fe20831-e59b-4878-82c5-53e47f13b524","id":"7fe20831-e59b-4878-82c5-53e47f13b524","bytes":11,"sha256":"443db030b4ce2bb6f7e0b25d48afe6cb7491f07e79122ed1c7d7e1b958f1a67e"}

Uppercase hex is rejected:

$ curl -s -X PUT "https://api2.infiniteocean.io/v2/blob/_docs_probe/files?id=7FE20831-E59B-4878-82C5-53E47F13B524" \
    -H "X-Ocean-Key: $OCEAN_KEY" --data-binary 'x'

{"ok":false,"error":"malformed blob id (need 32-hex or v1 dashed UUID) — nothing was stored","code":"BAD_BLOB_ID"}

If you send both ?id= and X-Blob-Id, the header wins — verified by sending ?id=a1a1… with X-Blob-Id: b2b2… and getting back "id":"b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2".

Re-uploading the same id replaces the content. There is no conflict error and no version history for blobs. The second PUT wins:

$ curl -s -X PUT "https://api2.infiniteocean.io/v2/blob/_docs_probe/files?id=00112233445566778899aabbccddeeff" \
    -H "X-Ocean-Key: $OCEAN_KEY" -H "Content-Type: text/plain" \
    --data-binary 'REPLACED body v2'
{"url":"/v2/blob/_docs_probe/files/00112233445566778899aabbccddeeff","key":"b/_docs_probe/files/00112233445566778899aabbccddeeff","id":"00112233445566778899aabbccddeeff","bytes":16,"sha256":"414aebc9732a808c042d42b1edd75f2411e80310c5410863ec1fe7530428d07d"}

$ curl -s https://api2.infiniteocean.io/v2/blob/_docs_probe/files/00112233445566778899aabbccddeeff \
    -H "X-Ocean-Key: $OCEAN_KEY"
REPLACED body v2

Every upload also writes a drop

So that uploads are listable, each successful PUT appends a drop on the same route under the key blobs/{id}. You do not have to write it and you cannot turn it off. Its payload, taken from a real /v2/list/ response:

{
  "type": "text/plain",
  "id":   "00112233445566778899aabbccddeeff",
  "_blob": {
    "id":      "00112233445566778899aabbccddeeff",
    "key":     "b/_docs_probe/files/00112233445566778899aabbccddeeff",
    "url":     "/v2/blob/_docs_probe/files/00112233445566778899aabbccddeeff",
    "bytes":   16,
    "type":    "text/plain",
    "sha256":  "414aebc9732a808c042d42b1edd75f2411e80310c5410863ec1fe7530428d07d"
  }
}

See Listing what you have uploaded for how to read these back, and for the two things this drop does not record.

Reading

GET /v2/blob/{route}/{id}

Returns the raw bytes with the Content-Type the blob was uploaded with.

$ curl -s -D- https://api2.infiniteocean.io/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183 \
    -H "X-Ocean-Key: $OCEAN_KEY"

HTTP/2 200
content-type: text/plain
content-length: 28
access-control-allow-origin: *
access-control-expose-headers: Content-Range, Location
x-content-type-options: nosniff

hello from the v2 blob door

The last path segment is always the id and everything before it is the route, so a nested route works without any encoding. An upload to /v2/blob/_docs_probe/a/b/c is read back at /v2/blob/_docs_probe/a/b/c/{id}, verified end to end. A read path with only one segment after /v2/blob/ has no id and returns 400 BAD_PATH.

A blob is readable from every node as soon as the PUT returns. Immediately after an upload we read the new id through all five origins by IP and got 200 from each — de, fi, ash, hil, sin.

Range requests

Send a Range: bytes=start-end header to fetch part of a blob. The response is 206 with a Content-Range giving the total size. Content-Range is in Access-Control-Expose-Headers, so browser JavaScript can read it.

$ curl -s -D- https://api2.infiniteocean.io/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183 \
    -H "X-Ocean-Key: $OCEAN_KEY" -H 'Range: bytes=0-4'

HTTP/2 206
content-type: text/plain
content-length: 5
content-range: bytes 0-4/28

hello

Open-ended ranges work and are clamped to the end of the blob:

$ curl -s -D- ... -H 'Range: bytes=6-'
HTTP/2 206
content-length: 22
content-range: bytes 6-27/28

from the v2 blob door

Three range behaviours will surprise you if you assume RFC 7233:

HEAD is not supported

HEAD /v2/blob/{route}/{id} returns 405 with an empty body. To check existence or size without downloading, use a one-byte range request and read the total out of Content-Range: Range: bytes=0-0 returns 206 with content-range: bytes 0-0/28. You can also read the blobs/{id} drop, which carries bytes.

There is no ETag on the authenticated read and no conditional-request support: an If-None-Match header is ignored and the full body is returned with 200.

Deleting

DELETE /v2/blob/{route}/{id}

Removes the blob. Returns 204 with an empty body.

$ curl -s -i -X DELETE https://api2.infiniteocean.io/v2/blob/_docs_probe/files/547625151cdcc6a7bbf4eb789a0b1c68 \
    -H "X-Ocean-Key: $OCEAN_KEY"
HTTP/2 204

$ curl -s https://api2.infiniteocean.io/v2/blob/_docs_probe/files/547625151cdcc6a7bbf4eb789a0b1c68 \
    -H "X-Ocean-Key: $OCEAN_KEY"
{"ok":false,"error":"blob not found","code":"NOT_FOUND"}

Four things to know:

Public blobs

A public blob is served from a second, keyless URL under /pub/. Publishing is explicit and per blob: a blob you have not published returns 404 at the public URL, with the same body as an id that never existed, so the gateway does not reveal which private ids are real.

Publishing at upload time: ?public=true

$ curl -s -X PUT "https://api2.infiniteocean.io/v2/blob/_docs_probe/files?public=true" \
    -H "X-Ocean-Key: $OCEAN_KEY" -H "Content-Type: application/json" \
    --data-binary '{"logo":"v2","n":1}'

{"url":"/v2/blob/_docs_probe/files/547625151cdcc6a7bbf4eb789a0b1c68","key":"b/_docs_probe/files/547625151cdcc6a7bbf4eb789a0b1c68","id":"547625151cdcc6a7bbf4eb789a0b1c68","bytes":19,"sha256":"79065239ab64d3ee5829c798e340a47c8f55257882f69771a3b64c24356e951e"}

The response is identical in shape to a private upload — it carries no flag saying the blob is public. You know it is public because you asked for it.

?public=true combines with ?id=:

$ curl -s -X PUT "https://api2.infiniteocean.io/v2/blob/_docs_probe/files?id=abcdef0123456789abcdef0123456789&public=true" \
    -H "X-Ocean-Key: $OCEAN_KEY" -H "Content-Type: text/css" \
    --data-binary 'body{color:#111}'

{"url":"/v2/blob/_docs_probe/files/abcdef0123456789abcdef0123456789","key":"b/_docs_probe/files/abcdef0123456789abcdef0123456789","id":"abcdef0123456789abcdef0123456789","bytes":16,"sha256":"cc564ca3bf4f4abe12c0ddee7a867bfe198d410fa4bd3f81c03653119091ecb8"}

The public URL: GET /pub/{route}/{id}

No key, no headers. Both api2.infiniteocean.io and view2.infiniteocean.io serve it, and all five origins answered 200 when queried by IP.

$ curl -s -D- https://api2.infiniteocean.io/pub/_docs_probe/files/547625151cdcc6a7bbf4eb789a0b1c68

HTTP/2 200
content-type: application/json
content-length: 19
access-control-allow-origin: *
access-control-expose-headers: Content-Range, Location
cache-control: public, max-age=31536000, immutable
x-content-type-options: nosniff

{"logo":"v2","n":1}

Note the path is /pub/, not /v2/blob/. The url field returned by the upload is always the private, authenticated path; build the public one yourself as https://api2.infiniteocean.io/pub/{route}/{id}.

A blob that is not public returns 404 here:

$ curl -s https://api2.infiniteocean.io/pub/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183
{"ok":false,"error":"not found","code":"NOT_FOUND"}

The response carries cache-control: public, max-age=31536000, immutable. Since ids are never reused unless you re-PUT them, that is safe for content — but it also means any browser or CDN that has cached a public blob may keep serving it after you make it private. Treat "make private" as a slow revocation, not an instant one, and use a fresh id if you need something to be unreachable immediately.

PUT /v2/blob/{route}/{id}/public

Publishes a blob that already exists.

$ curl -s -X PUT https://api2.infiniteocean.io/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183/public \
    -H "X-Ocean-Key: $OCEAN_KEY"

{"ok":true,"route":"_docs_probe/files","id":"5fb4d226afcf4e70dbaeb37872567183","public":true}

The keyless URL then returns the bytes. Publishing an id that does not exist returns 404:

$ curl -s -X PUT https://api2.infiniteocean.io/v2/blob/_docs_probe/files/cccccccccccccccccccccccccccccccc/public \
    -H "X-Ocean-Key: $OCEAN_KEY"
{"ok":false,"error":"blob not found — nothing was applied","code":"NOT_FOUND"}

The endpoint takes no query parameters — any parameter is a 400 UNKNOWN_PARAM, including plausible ones like ?ttl=60.

DELETE /v2/blob/{route}/{id}/public

Un-publishes: removes the public copy and leaves the private blob intact.

$ curl -s -X DELETE https://api2.infiniteocean.io/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183/public \
    -H "X-Ocean-Key: $OCEAN_KEY"
{"ok":true,"route":"_docs_probe/files","id":"5fb4d226afcf4e70dbaeb37872567183","public":false}

$ curl -s https://api2.infiniteocean.io/pub/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183
{"ok":false,"error":"not found","code":"NOT_FOUND"}

$ curl -s https://api2.infiniteocean.io/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183 \
    -H "X-Ocean-Key: $OCEAN_KEY"
hello from the v2 blob door

Content types

Whatever you send as Content-Type on the PUT is stored and replayed on every read, on both the authenticated and the public path. Nothing is sniffed or corrected — a PNG uploaded as text/plain came back as content-type: text/plain.

Omit the header and the blob is stored as application/octet-stream:

$ curl -s -X PUT "https://api2.infiniteocean.io/v2/blob/_docs_probe/files?id=11111111222233334444555555555555" \
    -H "X-Ocean-Key: $OCEAN_KEY" -H 'Content-Type:' --data-binary 'no content type here'
{"url":"/v2/blob/_docs_probe/files/11111111222233334444555555555555","key":"b/_docs_probe/files/11111111222233334444555555555555","id":"11111111222233334444555555555555","bytes":20,"sha256":"2b49932fc0b3b39ce43ef0ba7f39b75fab27863e239d7b0f534ecbac08619b67"}

$ curl -s -D- ... | grep content-type
content-type: application/octet-stream

Every response also carries x-content-type-options: nosniff, so a browser will not second-guess the type you set. If you serve user-uploaded files publicly, set the type deliberately — do not pass through a client-supplied one you have not validated.

Binary is exact. A 129-byte PNG and a 1 MiB random file were both uploaded and downloaded again and compared byte-identical with cmp, with the server's sha256 matching the local one in each case:

$ curl -s -o back.png -w '%{http_code} %{content_type} %{size_download}\n' \
    https://api2.infiniteocean.io/v2/blob/_docs_probe/files/653fb15d17e7263ae33620b260a55408 \
    -H "X-Ocean-Key: $OCEAN_KEY"
200 image/png 129

$ cmp tiny.png back.png && echo IDENTICAL
IDENTICAL

Listing what you have uploaded

There is no "list blobs" endpoint. Use GET /v2/list/{route} and read the blobs/ keys that every upload writes:

$ curl -s "https://api2.infiniteocean.io/v2/list/_docs_probe/files?limit=2" \
    -H "X-Ocean-Key: $OCEAN_KEY"

{"ok":true,"items":[
  {"key":"blobs/00112233445566778899aabbccddeeff","ts":1787593183490,
   "value":{"type":"text/plain","id":"00112233445566778899aabbccddeeff",
     "_blob":{"id":"00112233445566778899aabbccddeeff","key":"b/_docs_probe/files/00112233445566778899aabbccddeeff",
       "url":"/v2/blob/_docs_probe/files/00112233445566778899aabbccddeeff","bytes":16,"type":"text/plain",
       "sha256":"414aebc9732a808c042d42b1edd75f2411e80310c5410863ec1fe7530428d07d"}}},
  {"key":"blobs/11111111222233334444555555555555","ts":1787593189442,
   "value":{"type":"application/octet-stream","id":"11111111222233334444555555555555",
     "_blob":{"id":"11111111222233334444555555555555","key":"b/_docs_probe/files/11111111222233334444555555555555",
       "url":"/v2/blob/_docs_probe/files/11111111222233334444555555555555","bytes":20,"type":"application/octet-stream",
       "sha256":"2b49932fc0b3b39ce43ef0ba7f39b75fab27863e239d7b0f534ecbac08619b67"}}}
],"returned":2,"complete":false,"index":true,"next_cursor":"blobs/11111111222233334444555555555555"}

Items come back ordered by key, and next_cursor feeds the ?cursor= parameter for the next page. Your own drops on the same route are interleaved with the blobs/ ones.

Two limits of that drop, both of which bite in practice:

So write your own drop for anything you need to query on, and reference the blob by id:

$ curl -s -X POST https://api2.infiniteocean.io/v2/drop \
    -H "Content-Type: application/json" -H "X-Ocean-Key: $OCEAN_KEY" \
    -d '{"route":"_docs_probe/files","key":"doc-42",
         "payload":{"filename":"report.pdf",
                    "blob_id":"5fb4d226afcf4e70dbaeb37872567183",
                    "uploaded_by":"docs-probe"}}'

{"ok":true,"seq":1787577342969576,"offset":0,"len":166,"ts":1787593855218,"state":"accepted","rule_created":false,"drop_id":"1787577342969576","route":"_docs_probe/files","key":"doc-42"}

$ curl -s https://api2.infiniteocean.io/v2/entity/_docs_probe/files/doc-42 -H "X-Ocean-Key: $OCEAN_KEY"
{"filename":"report.pdf","blob_id":"5fb4d226afcf4e70dbaeb37872567183","uploaded_by":"docs-probe"}

Note that you cannot read the auto-written metadata drop through /v2/entity/: its key contains a slash (blobs/{id}), and /v2/entity/ treats the last path segment as the key, so /v2/entity/_docs_probe/files/blobs/{id} is parsed as a different route and returns 403 FORBIDDEN. Reach those drops through /v2/list/.

Access control

Blob permissions are the route's permissions — the same access rules that govern drops on that route. A key that is not a principal on the route gets 403 on every blob operation:

$ curl -s https://api2.infiniteocean.io/v2/blob/cc/log/ffffffffffffffffffffffffffffffff \
    -H "X-Ocean-Key: $OCEAN_KEY"
{"ok":false,"error":"authenticated key is not a principal on this route","code":"NOT_PRINCIPAL"}

The first upload to a route that does not exist yet creates it and claims it for your key. Reading the route's access afterwards showed your key as owner, enforced: true and no shares:

$ curl -s -X PUT https://api2.infiniteocean.io/v2/blob/_docs_probe/fresh-1787593351 \
    -H "X-Ocean-Key: $OCEAN_KEY" -H "Content-Type: text/plain" \
    --data-binary 'first upload claims the route'
{"url":"/v2/blob/_docs_probe/fresh-1787593351/fc099f4fa761a4ab1e063c16807f3870", ... }

$ curl -s "https://api2.infiniteocean.io/v2/access/_docs_probe%2Ffresh-1787593351" \
    -H "X-Ocean-Key: $OCEAN_KEY"
{"ok":true,"route":"_docs_probe/fresh-1787593351","owner":"9a92e0aed98f","owner_handle":"…","enforced":true,"shares":[]}

Errors

Errors are JSON with ok, error and a machine-readable code. Every one of these was produced against the live door.

StatuscodeWhen
400EMPTY_BODYPUT with a zero-length body — or a chunked body the server could not read. Nothing was stored.
400BAD_BLOB_ID?id= or X-Blob-Id is not 32 lowercase hex or a lowercase dashed UUID. Nothing was stored.
400BAD_RANGEThe Range header is not bytes=start-end or bytes=start-. Suffix ranges land here too.
400BAD_PATHA read or delete path that does not name a route and an id — e.g. GET /v2/blob/onlyone.
400UNKNOWN_PARAMAny query parameter the endpoint does not define.
401KEY_REQUIREDNo X-Ocean-Key header on a /v2/blob/ request.
401UNKNOWN_KEYThe key is not recognised.
403NOT_PRINCIPALValid key, but not a principal on that route.
404NOT_FOUNDNo blob with that id (authenticated GET), or the blob is not public (/pub/), or the id does not exist on the publish door. Also returned for a method/path with no endpoint, e.g. POST /v2/blob/{route}.
405—HEAD on a blob. Empty body.
413PAYLOAD_TOO_LARGERequest body over 4,194,304 bytes. The response carries limit_bytes. Nothing was stored.
503TRANSIENTA range whose start is at or past the end of the blob. Also the genuine "storage unavailable, retry" case — the two are indistinguishable from the response, so validate your offsets.

Unknown query parameters are rejected, not ignored

Every /v2/ endpoint 400s on a parameter it does not define. The blob upload defines exactly two — id and public — and every other blob endpoint defines none. So a plausible-looking parameter fails loudly rather than being silently dropped:

$ curl -s "https://api2.infiniteocean.io/v2/blob/_docs_probe/files/5fb4d226afcf4e70dbaeb37872567183?download=true" \
    -H "X-Ocean-Key: $OCEAN_KEY"
{"ok":false,"error":"unknown query parameter \"download\" — nothing was applied","code":"UNKNOWN_PARAM"}

$ curl -s -X PUT "https://api2.infiniteocean.io/v2/blob/_docs_probe/files?filename=x.txt" \
    -H "X-Ocean-Key: $OCEAN_KEY" --data-binary 'x'
{"ok":false,"error":"unknown query parameter \"filename\" — nothing was applied","code":"UNKNOWN_PARAM"}

Things that do not exist

Listed because a developer coming from S3 or from another file API will look for them.

Base URL https://api2.infiniteocean.io · authenticate with the X-Ocean-Key header.