Download a file's bytes
files.content
GET/api/v1/households/{household_id}/files/{file_id}/content
- Route
Streams the display copy, or the original when there is none. copy picks one, and download=1 asks the browser to save it. Supports a single Range, ETag revalidation with If-None-Match, and HEAD. Only inline types (images, PDFs, video, audio) are served as themselves; anything else is application/octet-stream as an attachment. See the files guide.
Parameters#
| Field | Description |
|---|---|
household_idpathstringrequired | The household's ID. |
file_idpathstringrequired | The file's ID, such as fil_01JC… |
copyqueryone of: original, displayoptional | Which copy. Defaults to the display copy, else the original. |
downloadqueryone of: 0, 1optional | 1 to have the browser save the file instead of showing it. |
Rangeheaderstringoptional | A single byte range: bytes=0-99, bytes=100- or bytes=-100. |
If-None-Matchheaderstringoptional | An ETag from an earlier response. |
Response#
200 The bytes.
The body is the raw bytes, as */*.
Errors#
Every error has the same envelope: error.code, error.message, error.details, error.recovery and error.request_id.
| Status | Code | When |
|---|---|---|
400 | validation_failed | A field is missing, misspelled or out of range. details.fields lists each problem. |
401 | unauthenticated | The request has no valid session. |
404 | not_found | The household doesn't exist, or you aren't a member of it. |
416 | Read error.code | Described above. |
Examples#
These are real. Each was recorded from a sample household, the Okafors, running through the whole API when this site was built.
Download a file
Without copy, you get the display copy, or the original when there isn't one.
curl 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content' \
-H "Cookie: rigel_session=$RIGEL_SESSION"const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content", {
headers: {
Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
},
});
const data = await response.json();GET /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 342
X-Request-Id: req_01M6V9CS9S7MMQHMTXRJPYPGXJ
ETag: "display-01M6V9CS9KGTGQ98QMTE123W11"
Content-Disposition: inline; filename="pantry-middle-shelf.png"; filename*=UTF-8''pantry-middle-shelf.png
(342 bytes of image/png; The file's bytes.)Download the original
download=1 asks for an attachment, so a browser saves the file instead of showing it.
curl 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content?copy=original&download=1' \
-H "Cookie: rigel_session=$RIGEL_SESSION"const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content?copy=original&download=1", {
headers: {
Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
},
});
const data = await response.json();GET /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content?copy=original&download=1 HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>HTTP/1.1 200 OK
Content-Type: image/png
Content-Length: 655
X-Request-Id: req_01M6V9CS9WJWH4MZ2BVDEWZMZX
ETag: "original-01M6V9CS9AY558G6KYPK0VRFC5"
Content-Disposition: attachment; filename="pantry-middle-shelf.png"; filename*=UTF-8''pantry-middle-shelf.png
(655 bytes of image/png; The file's bytes.)Download part of a file
One byte range, as a video player asks for.
curl 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content' \
-H "Cookie: rigel_session=$RIGEL_SESSION" \
-H 'Range: bytes=0-99'const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content", {
headers: {
Range: "bytes=0-99",
Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
},
});
const data = await response.json();GET /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Range: bytes=0-99HTTP/1.1 206
Content-Type: image/png
Content-Length: 100
X-Request-Id: req_01M6V9CS9X7WFVYWKF5KAD7H3G
ETag: "display-01M6V9CS9KGTGQ98QMTE123W11"
Content-Range: bytes 0-99/342
Content-Disposition: inline; filename="pantry-middle-shelf.png"; filename*=UTF-8''pantry-middle-shelf.png
(100 bytes of image/png; The file's bytes.)Download a file that hasn't changed
Send the ETag you have as If-None-Match and the API answers 304 with no body.
curl 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content' \
-H "Cookie: rigel_session=$RIGEL_SESSION" \
-H 'If-None-Match: "display-01M6V9CS9KGTGQ98QMTE123W11"'const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content", {
headers: {
"If-None-Match": "\"display-01M6V9CS9KGTGQ98QMTE123W11\"",
Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
},
});
const data = await response.json();GET /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
If-None-Match: "display-01M6V9CS9KGTGQ98QMTE123W11"HTTP/1.1 304
X-Request-Id: req_01M6V9CS9YR96XCBJHBP4G4ZBC
ETag: "display-01M6V9CS9KGTGQ98QMTE123W11"
Content-Disposition: inline; filename="pantry-middle-shelf.png"; filename*=UTF-8''pantry-middle-shelf.pngA byte range past the end
curl 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content' \
-H "Cookie: rigel_session=$RIGEL_SESSION" \
-H 'Range: bytes=999999-'const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content", {
headers: {
Range: "bytes=999999-",
Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
},
});
const data = await response.json();GET /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01M6V9CS96RRJQEBVXVCKMMR0T/content HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>
Range: bytes=999999-HTTP/1.1 416 Range Not Satisfiable
Content-Type: application/json
X-Request-Id: req_01M6V9CS9ZEN0HZTPY4AJ1WH42
Content-Range: bytes */342
{
"error": {
"code": "range_not_satisfiable",
"message": "That byte range is outside the file.",
"details": {
"size": 342
},
"recovery": "Ask for a range within the file's size, or omit Range.",
"request_id": "req_01M6V9CS9ZEN0HZTPY4AJ1WH42"
}
}Download a file that doesn't exist
curl 'https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01JC0000000000000000000000/content' \
-H "Cookie: rigel_session=$RIGEL_SESSION"const response = await fetch("https://rigel.house/api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01JC0000000000000000000000/content", {
headers: {
Cookie: `rigel_session=${process.env.RIGEL_SESSION}`,
},
});
const data = await response.json();GET /api/v1/households/hh_01M6V9CS6GGVFCY7NAS8ZTQV95/files/fil_01JC0000000000000000000000/content HTTP/1.1
Host: rigel.house
Cookie: rigel_session=<your session>HTTP/1.1 404 Not Found
Content-Type: application/json
X-Request-Id: req_01M6V9CTCZSCMKTM461EX0QE6Y
{
"error": {
"code": "not_found",
"message": "That file wasn't found.",
"details": {},
"recovery": "Check the ID. You can only reach households you're a member of.",
"request_id": "req_01M6V9CTCZSCMKTM461EX0QE6Y"
}
}