Manage FrameCast Files from your own code. Make folders, upload and download files of any size, rename, move, search, and keep a copy in sync, in My Files or any team you're in. Everything you do through the API shows up straight away on the web, Mac, and iPhone.
Make a key. Sign in and open Account › API keys. Choose Read and write, then copy the key. It starts with fcsk_ and is shown only once. Keep it in an environment variable:
Send your key with every request, in the Authorization header:
Header
Authorization: Bearer fcsk_…
X-API-Key: fcsk_… works too. Requests without a working key get 401 unauthorized.
A key acts as you. It can never do more than your account can. In a team, your role, restricted folders, and the team's plan all still apply, so a key made by a team viewer can only read. Scopes and drive limits narrow a key further; they never widen it.
Keep keys secret and on servers. Don't put a key in a web page, a browser extension, a mobile app, or a public repository. The API doesn't allow cross-origin browser requests. If a key might have leaked, revoke or rotate it under Account › API keys; it stops working on its next request.
Keys are fcsk_ followed by 46 letters and digits, the last 6 being a checksum. FrameCast keeps only a one-way fingerprint of each key, so nobody at FrameCast can see or recover yours.
Scopes decide what a key can do. You choose them when you make the key, and you can change them at any time without replacing it. Some scopes include others, as shown below.
Scope
Allows
Includes
files:read
List drives and folders, search, read file details, download, and follow the change feed.
—
files:write
Upload files and new versions, make folders, rename, and move.
files:read
files:delete
Move items to Trash and bring them back.
files:read
files:purge
Permanently delete items in Trash. This can't be undone.
files:readfiles:delete
access:manage
Restrict team folders and choose who sees them. Only works in teams where you're an owner or admin.
files:read
Presets: Read only (files:read); Read and write (files:read, files:write); Full access (files:read, files:write, files:delete, files:purge, access:manage). Each endpoint below lists the scope it needs. Without it, the answer is 403 insufficient_scope.
Drive limits. Limit a key to My Files, to particular teams, or let it reach every drive, including teams you join later. Anything outside its drives answers 404, so a key never learns what exists elsewhere.
Expiry. Keys last 7, 30, 90, or 365 days, or never expire. The default is 90 days.
Rotation. Rotating issues a new key with the same scopes and drives. The old key stops at once, or keeps working for up to 7 days while you deploy the new one.
Revoking stops a key on its next request.
Last used. Each key shows when and from which address it was last used.
You can have 25 working keys. Keys can only be made and changed by someone signed in, never with another key.
X-RateLimit-Reset is the number of seconds until the count starts again. Over the limit, requests get 429 rate_limited with a Retry-After header; wait that many seconds before trying again. An address that sends 30 keys that don't work within a minute is blocked for the rest of that minute. Uploading parts to their signed URLs doesn't count against the limit.
Lists come in pages. Pass the answer's nextOffset as offset to get the next page, and stop when it's null. Folder listings take a limit of up to 1000; the default is 100.
Every upload names its destination, with parentId (a folder) or driveId (the top of a drive), so nothing lands anywhere you didn't choose. Uploads count against the storage of the space they go into.
Up to 4 MB: one request
Send the file as the body of POST /uploads/direct, with the name in the query string. It answers with the new file.
Any size, up to 50 GB: in parts
Start the upload with the file's name and exact size. The answer gives a partSize, a partCount, and signed URLs for the first 100 parts.
Send part n, bytes (n − 1) × partSize up to n × partSize, as a PUT to its URL, without your API key. Keep the ETag header from each answer. Parts can go in parallel and be retried.
Past part 100, or after a URL expires (they last 60 minutes), ask for more URLs.
To replace a file's contents, start a multipart upload with replaces set to the file's id. The file keeps its id, name, and place, and its rev goes up. Send the rev you last saw as baseRev; if someone saved in the meantime, you get a 409 instead of overwriting their work.
Names and types
A file name already used in the folder gets a number, such as photo (2).jpg. If the content type is missing or generic, such as application/octet-stream or curl's default form type, it's worked out from the file's extension.
Give each upload a sourceKey: your own id for the file, 8 to 300 characters of letters, digits, and . _ : / + = @ -, such as camera-7:IMG_0001. If that key is already in the drive, the upload answers { "duplicate": true, "item": … } and stores nothing. That includes files moved to Trash, so a retried or re-run backup never stores the same file twice.
To keep a copy of a drive up to date, call the change feed without a cursor. It answers reset: true and a cursor. List the drive from its rootId, then call the feed with that cursor every so often. Each change is an upsert with the item as it now is, or a delete with an id. Download a file again only when its contentId changes. If the feed answers reset: true again, list the drive again.
The calling key's scopes, the drives it's limited to (null means every drive), its expiry, and the account it acts for. Any working key can call it, so it's a good first request to check a key works.
What changed in a drive since a cursor, for keeping a copy in sync. The first call, without a cursor, answers reset: true: list the drive from the top, then keep calling with the returned cursor. A reset later means the cursor was too old, so list again. If hasMore is true, call again straight away.
Deletes one item in Trash, and everything inside it, for good. Or, with all=1, empties the drive's whole Trash (in a team, admins only). all=1 is required so that a missing item can never empty everything. This can't be undone.
Path
driveIdstringrequired
The drive.
Query
itemstring
The item in Trash to delete.
all1
Send all=1 instead of item to empty the whole Trash.
What's in a folder: folders first, then files. Pass a drive's rootId to list the top of the drive. The answer also gives the path from the top and what you can do in the folder.
Path
folderIdstringrequired
A folder's id, or a drive's rootId.
Query
sortstring
name (default), date (newest first), or size (largest first).
limitinteger
How many to return, 1–1000. Default 100.
offsetinteger
Where to continue from. Use the previous answer's nextOffset.
A signed link to the file's bytes, good for an hour. Fetch it without the API key. Add redirect=1 to get a 302 to the link instead, so curl -L -o saves the file in one step. A preview or thumbnail that hasn't been made yet answers 202 with { "pending": true }.
Path
fileIdstringrequired
The file's id.
Query
variantstring
original (default), preview (up to 1600 px), or thumb (up to 480 px).
download1
Save it under its name rather than open it in a browser.
Renames an item, moves it to another folder, or both. searchText sets extra words that search matches, such as text read from a photo (files only). The request is applied whole or not at all. Managed folders such as Screenshots can't be renamed or moved.
Path
itemIdstringrequired
The item's id.
JSON body
namestring
The new name. A name already used in the folder is refused with 409.
parentIdstring
The folder to move it into.
searchTextstring
Words search should find this file by, up to 20,000 characters. An empty string clears it.
Restricts a team folder to the people granted it, or opens it to the whole team. Grants a member view or edit access, or removes it. Owners and admins always see everything.
Path
folderIdstringrequired
A team folder.
JSON body
restrictedboolean
true to restrict the folder, false to open it to the team.
Send name to make one folder. A name already used in that folder is refused with 409, unless you also send reuse: true, which returns the existing folder. Or send path to make every missing folder along it, like mkdir -p. That reuses folders that exist and returns the last one, so it's safe to repeat.
JSON body
parentIdstring
The folder it goes into. Send this or driveId.
driveIdstring
The top of this drive. Send this or parentId.
namestring
The folder's name. Send this or path.
pathstring
Folder names separated by /, up to 32 deep. Send this or name.
reuseboolean
With name: return the existing folder rather than a 409.
Uploads a file of up to 4 MB in one request, with the file itself as the request body. A file name already taken in the folder gets a number, such as photo (2).jpg.
Query
parentIdstring
The folder it goes into. Send this or driveId.
driveIdstring
The top of this drive. Send this or parentId.
namestringrequired
The file's name, with its extension.
sourceKeystring
Your own unique id for the file, to prevent duplicates. See duplicates.
capturedAtdate
When the photo or video was taken (ISO 8601). Search can filter by it.
Body
The raw file bytes. Set Content-Type to the file's type. If it's missing or generic, the type is worked out from the name.
Starts an upload of up to 50 GB. The answer includes signed URLs for the first 100 parts. PUT each part to its URL, then complete the upload. To upload a new version of a file, send replaces instead of a destination; it keeps the file's id and name. If the sourceKey is already in the drive, the answer is { "duplicate": true, "item" } and there's nothing to send.
JSON body
parentIdstring
The folder it goes into. Send this or driveId.
driveIdstring
The top of this drive. Send this or parentId.
namestring
The file's name. Required unless replaces is sent.
bytesintegerrequired
The file's exact size in bytes.
contentTypestring
The file's type. If it's missing or generic, it's worked out from the name.
replacesstring
A file to upload a new version of, instead of parentId or driveId.
baseRevinteger
With replaces: the file's rev when you last read it. If someone saved since, you get a 409 instead of overwriting their change.
sourceKeystring
Your own unique id for the file, to prevent duplicates.
Finishes the upload and files it in its folder. List every part with the ETag header its PUT returned. If you uploaded a new version but someone else saved first, the answer is a conflicted copy saved beside the file.
Path
uploadIdstringrequired
The upload's id.
JSON body
partsobject[]required
{ partNumber, etag } for every part, from 1 to partCount.
2026-10-31 · Version 1: drives, folders, items, uploads, downloads, Trash, search, the change feed, and folder access, with scoped API keys.
Changes within /api/v1 only add things: new endpoints, new optional parameters, and new fields. Anything that would break existing code will come as /api/v2.
Help us measure our ads?
With your permission, Meta and Trybe receive visits to our marketing pages and browser identifiers. Meta receives confirmed Pro or Team purchase amounts; Trybe receives paid checkout amounts to credit creators for sales. Recording and editing work either way. You can change your choice anytime. Privacy details