Seller API
Add and update your shop's listings from your own tools — printed pieces, digital 3D files, or both. Everything the listing form in Shop Manager does, in one request.
1. Your API key
In Shop Manager → Settings → API keys, write what the key is for and press Create key. The key starts with fgk_live_ and is shown once — copy it then. It belongs to the shop that is selected when you make it; if you run several shops, make one key in each. A shop can have five keys; Revoke stops one at once.
Send the key with every request, and keep it on a server — anyone who has it can list in your shop:
Authorization: Bearer fgk_live_…
Base address: https://www.filagora.com/api/v12. Quick start
A printed piece
curl https://www.filagora.com/api/v1/listings \
-H "Authorization: Bearer $FILAGORA_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "vase-001",
"title": "Spiral Bud Vase, 15 cm",
"description": "A twisted bud vase printed in matte PLA.\n\nWatertight with the glass insert.",
"category": "vases-planters",
"tags": ["vase", "spiral", "minimal"],
"images": ["https://example.com/vase-front.jpg", "https://example.com/vase-side.jpg"],
"physical": { "price": 24, "colors": ["White", "Sage"], "materials": ["PLA"], "leadDays": 3 },
"designSource": "own",
"madeBy": "shop",
"standardsOk": true
}'{
"slug": "spiral-bud-vase-15-cm",
"status": "active",
"created": true,
"publishAt": null,
"url": "https://www.filagora.com/product/spiral-bud-vase-15-cm"
}A digital file
The same listing with digital instead of physical. Buyers download the files right after they pay.
{
"externalId": "vase-001-file",
"title": "Spiral Bud Vase — STL file",
"description": "Print it yourself: 15 cm tall, no supports, about 3 hours.",
"category": "vases-planters",
"tags": ["vase", "stl", "vase mode"],
"images": ["https://example.com/vase-front.jpg"],
"digital": {
"price": 4.5,
"license": "personal",
"files": [
{ "url": "https://example.com/files/spiral-vase.stl" },
{ "url": "https://example.com/files/print-guide.pdf" }
]
},
"designSource": "own",
"madeBy": "shop",
"standardsOk": true
}{
"slug": "spiral-bud-vase-stl-file",
"status": "active",
"created": true,
"url": "https://www.filagora.com/product/spiral-bud-vase-stl-file",
"files": [
{ "name": "spiral-vase-1a2b3c4d.stl", "format": "stl", "size": 1840584, "status": "new" },
{ "name": "print-guide-9f8e7d6c.pdf", "format": "pdf", "size": 210233, "status": "new" }
]
}Send physical and digital together and buyers choose: the printed piece, or the file.
3. Listing fields
| Field | What to send |
|---|---|
| externalId | Required. Your own id for the listing, up to 120 characters. Sending the same externalId again updates that listing. |
| title | Required. 3–120 characters. |
| description | Required. Up to 8,000 characters. The first line is the short summary; a blank line starts a new paragraph. |
| category | Required. One of the category slugs. |
| tags | Up to 13 words or short phrases buyers search for. |
| images | Required. 1–10 photos, the first is the cover. See Photos. |
| physical | The printed piece: price in US dollars (1–100,000), colors (up to 12), materials, leadDays (days to make it, 1–60). |
| digital | The file: price in US dollars (0–100,000), license "personal" or "commercial", includesProfile (true if print profiles are included), files. See Digital files. |
| designSource | Required. "own" you designed it · "open" a credited remix of an openly licensed model · "licensed" you hold a licence to sell prints (printed only — files can't be resold). |
| designCredit | Required for open and licensed: the designer and the licence. |
| madeBy | "shop" you print it · "partner" a production partner prints it (then productionPartner: who and where). |
| printShare · printTech | printShare "all" (default) or "most" of the piece is printed · printTech any of fdm resin powder metal other. |
| standardsOk | Required on a new listing: true confirms it meets the Maker Standards. |
| personalization | Printed only: { "prompt": "Name to print", "max": 20, "required": true } with "personalizable": true. |
| status | "paused" saves the listing without showing it yet. |
| publishAt | A date and time (ISO 8601) to publish later, up to a year ahead. |
| video | A public mp4 or webm link, up to 50 MB — shown after the first photo. |
| printMinutes · filamentGrams | Shown on the listing when you send them. |
Shipping prices and delivery times come from your shop's settings, not from each listing.
4. Photos
Each item in images can be any of these — mix them freely:
| Way | Write it like |
|---|---|
| Link | "https://…/front.jpg" or { "url": "https://…/front.jpg" } |
| Base64 | "data:image/jpeg;base64,/9j/4AAQ…" or { "name": "front.jpg", "base64": "/9j/4AAQ…" } |
| Multipart | a file part named images (see multipart) |
| Upload | { "upload": "front-1a2b3c4d.jpg" } from a direct upload |
JPG, PNG, WebP, GIF or AVIF, up to 15 MB each. We store a 2000 px copy and an 800 px copy for cards. A photo you send again — the same link, the same bytes or the same upload — isn't copied again.
5. Digital files
| Format | For | We check |
|---|---|---|
| STL | the model (binary or ASCII) | a real STL structure |
| 3MF | the model, or a Bambu / Prusa project | a 3MF package with a model inside |
| OBJ | the model | vertex data |
| STEP / STP | CAD models | the ISO 10303-21 header |
| ZIP | several parts in one download | at least one STL, 3MF, OBJ or STEP inside, and no programs or scripts |
| an assembly or print guide | a real PDF — sent with a 3D file | |
| TXT | a readme, print settings, licence | plain UTF-8 text up to 2 MB — sent with a 3D file |
- Every digital listing needs at least one 3D file. Up to 20 files, each up to 50 MB.
- Every file is opened and checked before it's stored; a file that isn't what its name says is refused with the reason.
- Files are kept private. Buyers download them from their order; the name they see is your file's name plus a short code, like
spiral-vase-1a2b3c4d.stl. - Only designs that are yours (
own) or credited remixes of open models (open) can be sold as files.
Five ways to send a file
Each item in digital.files can be any of these:
| Way | Write it like | Best for |
|---|---|---|
| Link | "https://…/model.stl" or { "url": "…", "name": "model.stl" } | files already online. Google Drive ("Anyone with the link"), Dropbox and GitHub share links work as they are. |
| Base64 | { "name": "model.stl", "base64": "…" } | small files your code already holds |
| Plain text | { "name": "model.stl", "text": "solid model…" } | ASCII STL, OBJ, STEP and TXT |
| Multipart | file parts named files | sending files from disk, e.g. curl -F |
| Upload | { "upload": "model-1a2b3c4d.stl" } | files over ~3 MB — see direct upload |
A link must return the file itself, not a web page about it. If a name has no extension, add "format": "stl".
"digital": {
"price": 2,
"files": [
{ "name": "tetra.stl", "text": "solid tetra\n facet normal 0 0 1\n outer loop\n vertex 0 0 0\n vertex 10 0 0\n vertex 0 10 0\n endloop\n endfacet\nendsolid tetra\n" },
{ "name": "print-settings.txt", "text": "0.2 mm layers, 15% infill, no supports." }
]
}curl https://www.filagora.com/api/v1/listings \
-H "Authorization: Bearer $FILAGORA_KEY" \
-F 'listing={"externalId":"planter-01","title":"Hex Planter — 3MF and STL","description":"Two sizes in one project.","category":"vases-planters","digital":{"price":5},"designSource":"own","madeBy":"shop","standardsOk":true}' \
-F "images=@planter.jpg" \
-F "files=@planter.3mf" \
-F "files=@planter.stl" \
-F "files=@assembly.pdf"6. Big files: direct upload
One request to /listings carries about 4 MB in total, and base64 makes a file a third bigger. For anything larger, use a link — or put the file straight into storage in three steps:
curl https://www.filagora.com/api/v1/files \
-H "Authorization: Bearer $FILAGORA_KEY" \
-H "Content-Type: application/json" \
-d '{ "name": "dragon.stl", "size": 38200000 }'
{
"upload": "dragon-1a2b3c4d.stl",
"uploadUrl": "https://…",
"method": "PUT",
"headers": { "content-type": "application/octet-stream" },
"maxBytes": 52428800,
"expiresInSeconds": 7200
}curl -X PUT "$UPLOAD_URL" -H "Content-Type: application/octet-stream" --data-binary @dragon.stl"digital": { "price": 12, "files": [ { "upload": "dragon-1a2b3c4d.stl" } ] }GET /api/v1/files?upload=dragon-1a2b3c4d.stl tells you whether the file arrived and its size. Upload links last two hours. If the listing is refused — a brand name in the title, say — fix it and send it again with the same upload id. Photos work the same way: ask forcover.jpg and put { "upload": "cover-…jpg" } in images.
7. Updating a listing
- Send the whole listing again with the same
externalId. A field you leave out goes back to its default — your Maker Standards confirmation is kept. - Photos and files you send again unchanged (same link, same bytes or same upload) aren't downloaded again; the response marks those files
"kept". digitalwithoutfileskeeps the listing's files (handy for a price change).digitalwithfilesreplaces them, and the old ones are deleted.- Buyers who already bought the file get the new version from their order.
8. Read, pause, remove
| Request | Does |
|---|---|
| GET /listings?externalId=… | The listing: slug, status, title, images, physical (price, colors, materials), digital (price, formats, license, files). |
| PATCH /listings | { "externalId": "vase-001", "status": "paused" } — or "active" to show it again. |
| DELETE /listings?externalId=… | Removes the listing. Past orders keep working; the same externalId sent later makes a new listing. |
| GET /shop | Which shop the key belongs to: name, slug, logo, status. |
| GET /categories | The category slugs. No key needed. |
| /gifts | A free gift with a listing: POST { "productExternalId", "giftExternalId" }, GET lists them, DELETE ?productExternalId=… removes one. The gift must be one of your printed listings. |
9. Errors and limits
Errors come back as { "error": "…" } with a message that says what to fix, and nothing from a refused request is kept:
| Status | Means |
|---|---|
| 400 | Something in the request — the message names it. |
| 401 | The key is missing, mistyped or revoked. |
| 403 | The shop is suspended, the listing isn't one of this shop's, or the key can't do this. |
| 404 | GET found no listing with that externalId, or the upload hasn't arrived yet. |
| 413 | The request is over about 4 MB and was stopped before it reached us (the reply isn't JSON) — send big files as a link or a direct upload. |
| 500 | Our side. Wait a moment and send it again; nothing half-saved is left behind. |
- The same rules as the listing form: brand and character names, prohibited items and the Maker Standards apply to every request.
- A listing pays the 5¢ listing fee from your balance once, when it first goes live.
- If your shop hasn't opened yet, listings are saved as drafts and go live when it opens.
- Limits: 10 photos (15 MB each), 20 files (50 MB each), about 4 MB per request, 5 minutes per request.
10. Categories
| category | Name |
|---|---|
| vases-planters | Vases & Planters |
| lighting | Lamps & Lighting |
| desk-organization | Desk & Organization |
| home-decor | Home Decor |
| gadgets-stands | Gadgets & Stands |
| toys-figurines | Toys & Figurines |
| jewelry-wearables | Jewelry & Wearables |
| kitchen-bath | Kitchen & Bath |
| miniatures-tabletop | Miniatures & Tabletop |
| cosplay-props | Cosplay & Props |
| parts-repairs | Parts & Repairs |
| printer-upgrades | Printer Upgrades |
| garden-outdoor | Garden & Outdoor |
| pets | Pets |
| other | Other |
11. Code samples
import { readFile } from "node:fs/promises";
const stl = await readFile("spiral-vase.stl");
const res = await fetch("https://www.filagora.com/api/v1/listings", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.FILAGORA_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({
externalId: "vase-001-file",
title: "Spiral Bud Vase — STL file",
description: "Print it yourself: 15 cm tall, no supports.",
category: "vases-planters",
tags: ["vase", "stl"],
images: ["https://example.com/vase-front.jpg"],
digital: { price: 4.5, files: [{ name: "spiral-vase.stl", base64: stl.toString("base64") }] },
designSource: "own", madeBy: "shop", standardsOk: true,
}),
});
console.log(res.status, await res.json());import json, os, requests
listing = {
"externalId": "planter-01",
"title": "Hex Planter",
"description": "Printed in matte PLA, or print it yourself from the 3MF.",
"category": "vases-planters",
"tags": ["planter", "hexagon"],
"physical": {"price": 22, "colors": ["Sage", "White"]},
"digital": {"price": 5},
"designSource": "own", "madeBy": "shop", "standardsOk": True,
}
with open("planter.jpg", "rb") as photo, open("planter.3mf", "rb") as model:
r = requests.post(
"https://www.filagora.com/api/v1/listings",
headers={"Authorization": f"Bearer {os.environ['FILAGORA_KEY']}"},
data={"listing": json.dumps(listing)},
files=[("images", ("planter.jpg", photo)), ("files", ("planter.3mf", model))],
)
print(r.status_code, r.json())import os, requests
H = {"Authorization": f"Bearer {os.environ['FILAGORA_KEY']}"}
up = requests.post("https://www.filagora.com/api/v1/files", headers=H, json={"name": "dragon.stl"}).json()
with open("dragon.stl", "rb") as f:
requests.put(up["uploadUrl"], data=f, headers=up["headers"]).raise_for_status()
r = requests.post("https://www.filagora.com/api/v1/listings", headers=H, json={
"externalId": "dragon-01",
"title": "Articulated Dragon — print-in-place STL",
"description": "Prints in one piece, no supports.",
"category": "toys-figurines",
"images": ["https://example.com/dragon.jpg"],
"digital": {"price": 6, "files": [{"upload": up["upload"]}]},
"designSource": "own", "madeBy": "shop", "standardsOk": True,
})
print(r.status_code, r.json())Stuck on something? Use Live support at the bottom of any page, or write to us at support@filagora.com — send the request you made and the error you got.