Swipe file
A searchable library of reference images. Upload competitor ads and inspiration, then find them by natural-language search.
A swipe file is a lightweight, org-scoped library of static reference images — competitor ads, inspiration, anything you want to point at later. Each upload is embedded and auto-classified so you can retrieve it with filtered vector search. Unlike design templates, swipe files are just the image plus metadata — no JSON tree, no reverse-engineering pipeline.
Mental model
Use a swipe file when you want to stash a reference image and find it fast, without turning it into a structured, editable end-product. On upload, each image is:
- Stored (compressed, with a thumbnail).
- Embedded with a multimodal model into a shared text/image vector space.
- Auto-classified into one
industryand onead_formatfrom our standard taxonomies.
You then retrieve images with filtered vector search: a text query is embedded and cosine-matched against the image embeddings, optionally narrowed by industry, ad_format, and aspect_ratio.
Your library is the union of your own uploads and the data sources your organization is subscribed to — curated collections managed by Static Ads Lab that you can toggle on or off at any time.
Swipe files are free — there is no per-image charge.
ID prefix: sf_ (swipe files), sfs_ (data sources).
Fields
| Field | Type | Description |
|---|---|---|
id | string | sf_… |
image_url | string | URL of the stored image |
thumbnail_url | string | null | URL of a smaller thumbnail |
width / height | number | null | Pixel dimensions |
aspect_ratio | string | null | e.g. "1:1", "4:5", "9:16" |
industry | string | null | Auto-classified Shopify L1 category |
ad_format | string | null | Auto-classified creative format |
source | enum | upload | import | studio_concept | sal_managed |
is_managed | boolean | True for designs from a managed data source (read-only) |
status | enum | processing | ready | failed |
error | string | null | Failure reason when status is failed |
created_at / updated_at | string | ISO timestamps |
Lifecycle
stateDiagram-v2
[*] --> processing: POST /v1/swipe-files (multipart upload)
processing --> ready: embed + classify succeed
processing --> failed: embedding failed
ready --> [*]
failed --> [*]Ingest is asynchronous. The upload returns immediately with status: "processing"; the item becomes ready once its embedding and classification finish. A swipe file only appears in search results once it is ready. If classification degrades, the item still becomes ready with industry/ad_format left null — only an embedding failure marks it failed.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST | /v1/swipe-files | Upload a reference image (multipart) |
GET | /v1/swipe-files | List swipe files with filters |
GET | /v1/swipe-files/:id | Get a swipe file |
DELETE | /v1/swipe-files/:id | Archive (soft-delete) a swipe file |
POST | /v1/swipe-files/:id/restore | Restore an archived swipe file |
POST | /v1/swipe-files/search | Filtered vector search |
GET | /v1/swipe-files/data-sources | List data sources with subscription state |
PUT | /v1/swipe-files/data-sources/:id/subscription | Enable or disable a data source |
Data sources
Beyond your own uploads, your swipe file can pull from data sources: shared, Static Ads Lab-curated collections of reference ads. New organizations are automatically subscribed to the default collection, so your library is stocked from day one. Subscriptions are per-organization and idempotent to toggle.
- Designs from a subscribed source appear in your list and search results alongside your own uploads, marked with
is_managed: trueandsource: "sal_managed". - Managed designs are read-only: archiving, restoring, or deleting them returns
SWIPE_FILE_READ_ONLY. To hide them, disable the source. - Data sources are free — the curated content adds no charges to your account.
List available data sources
const response = await fetch("https://api.staticadslab.com/v1/swipe-files/data-sources", {
headers: { "X-API-Key": "YOUR_API_KEY" },
});
const { data } = await response.json();
for (const source of data) {
console.log(source.id, source.name, source.subscribed, source.item_count);
}
// sfs_all_sal_ads "All Static Ads Lab Ads" true 240Enable or disable a data source
await fetch(
"https://api.staticadslab.com/v1/swipe-files/data-sources/sfs_all_sal_ads/subscription",
{
method: "PUT",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({ active: false }), // true re-enables
},
);Scope list/search to specific sources
Both GET /v1/swipe-files and POST /v1/swipe-files/search accept data_source_ids and include_own:
const response = await fetch("https://api.staticadslab.com/v1/swipe-files/search", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "bold typography sale announcement",
data_source_ids: ["sfs_all_sal_ads"], // only this source's designs
include_own: false, // exclude my own uploads
}),
});Omit data_source_ids to search everything you are subscribed to; pass data_source_ids: [] with include_own: true to search only your own uploads.
Common patterns
Upload a reference image
Upload raw bytes directly with multipart/form-data — no need to pre-host the image. search and read require the swipe_files:read scope; uploads require swipe_files:write.
const form = new FormData();
form.append("file", fileBlob, "competitor-ad.png"); // File or Blob
const response = await fetch("https://api.staticadslab.com/v1/swipe-files", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
// Do NOT set Content-Type — the runtime adds the multipart boundary.
},
body: form,
});
const { data } = await response.json();
console.log(data.id, data.status); // sf_..., "processing"Then poll until ready:
async function waitForSwipeFile(id) {
while (true) {
const r = await fetch(
`https://api.staticadslab.com/v1/swipe-files/${id}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } },
);
const { data } = await r.json();
if (data.status === "ready") return data;
if (data.status === "failed") throw new Error(data.error ?? "Ingest failed");
await new Promise((res) => setTimeout(res, 2000));
}
}Search by description
const response = await fetch("https://api.staticadslab.com/v1/swipe-files/search", {
method: "POST",
headers: {
"X-API-Key": "YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
query: "minimalist beige flatlay with soft shadows",
ad_format: ["Sale", "Offer"], // optional filters; string or array (matches any)
aspect_ratio: "1:1",
limit: 20,
}),
});
const { data } = await response.json();
for (const hit of data) {
console.log(hit.id, hit.similarity, hit.image_url);
}Every metadata filter (industry, ad_format, aspect_ratio) accepts a single
string or an array of strings. Multiple values on one dimension match any of
them; different dimensions combine with AND.
List and filter without a query
// Repeat a query param to match any of multiple values on that dimension.
const params = new URLSearchParams({ status: "ready" });
params.append("industry", "Apparel & Accessories");
params.append("industry", "Health & Beauty");
const response = await fetch(
`https://api.staticadslab.com/v1/swipe-files?${params}`,
{ headers: { "X-API-Key": "YOUR_API_KEY" } },
);
const { data, has_more } = await response.json();Pitfalls
- Ingest is async. A newly uploaded swipe file is
processingand will not appear in/searchresults until it isready. PollGET /v1/swipe-files/:idor list withstatus=processingto track it. searchis aPOSTbut read-intent — it requires theswipe_files:readscope, notwrite.- Supported upload formats are PNG, JPEG, and WebP. WebP is transcoded to PNG before embedding.
- Your library is org-scoped plus any subscribed data sources. If unexpected designs appear (or disappear), check
GET /v1/swipe-files/data-sources— a subscription toggle changes list and search results immediately. - Managed designs (
is_managed: true) cannot be archived or restored —DELETEreturnsSWIPE_FILE_READ_ONLY. Disable the data source instead.
Related
Prompt for your agent
Read https://www.staticadslab.com/docs/resources/swipe-file.mdx and write a function that uploads a reference image to the swipe file via multipart POST /v1/swipe-files, polls until status is ready, then runs POST /v1/swipe-files/search with a text query and returns the top matches with their similarity scores.