Furlen

by Unknown

Not rated
Website

About

Turns public data (World Bank, IMF, FRED, Eurostat, SEC filings) or your own CSV into an animated chart video, recomputing every figure from the source rows before export.

Details

Author
Unknown
Categories
Design, Other

One call runs the whole pipeline: profile → insights → AI story → claim verification → render. Plan limits (watermark, resolution, render minutes) apply exactly as in-app.

CSV in, queued render out. Rate limit: 10 renders/min per workspace.

curl -X POST https://furlen.pro/api/v1/renders \ -H "Authorization: Bearer sg_live_..." \ -H "Content-Type: application/json" \ -d '{ "csv": "month,revenue\n2025-01,120000\n2025-02,145000\n2025-03,210000", "audience": "investor", "outputFormat": "video_16_9", "format": "mp4", "resolution": "1080p" }'

audience: investor · executive · linkedin · youtube · internal_team · client_report · student — outputFormat: video_16_9 · video_9_16 · video_1_1 — format: mp4 · gif — resolution: 720p · 1080p · 4K (capped by plan).

Poll every few seconds; renders typically take 30–90s.downloadUrlappears when completed.

A render moves throughqueued → running → completed, or endsfailed/cancelled. Treat any unknown status as “still working”. Example bodies:

// queued { "renderId": "3f9c…-uuid", "status": "queued", "progress": 0 } // running — progress is a percentage, 0..100 { "renderId": "3f9c…-uuid", "status": "running", "progress": 62 } // completed { "renderId": "3f9c…-uuid", "status": "completed", "progress": 100, "format": "mp4", "resolution": "1080p", "downloadUrl": "/api/render-jobs/3f9c…-uuid/download" } // failed — 'error' carries the reason; safe to retry { "renderId": "3f9c…-uuid", "status": "failed", "progress": 0, "error": "Render worker error — safe to retry", "downloadUrl": null }

Send anIdempotency-Keyon POST so a retried request never double-renders (and never double-charges render minutes) — the same key returns the same job.

POST /api/v1/renders Idempotency-Key: 9f2c-your-unique-key // Rate-limit headers on every response: X-RateLimit-Limit: 10 X-RateLimit-Remaining: 7 X-RateLimit-Reset: 1752531600 // unix seconds Retry-After: 42 // present on 429

Limits: CSV/JSON body up to 5 MB · 10 renders/min per workspace · resolution capped by plan (720p Free, 1080p Creator, 4K Pro+). Invalid input returns400 invalid_requestwith amessagenaming the offending field.

Every error returnserror(a human-readable message) andcode(stable — parse this one). Messages get reworded; codes do not.

401 { "error": "Invalid or missing API key.", "code": "unauthorized" } 403 { "error": "This API key does not have the 'renders:write' scope.", "code": "insufficient_scope" } 402 { "error": "This format requires a higher plan.", "code": "plan_gate" } 422 { "error": "No story-worthy insights found in this data.", "code": "no_insights" } 429 { "error": "Rate limit exceeded (10/min).", "code": "rate_limited" }

401, 403 and 402 are terminal — fix the key, the scope, or the plan. 429 is safe to retry onceRetry-Afterhas elapsed. 422 means the data itself can't carry a story; retrying the same CSV will fail identically.

Keys are scoped. Give a CI job a key that can onlystartrenders, and it cannot pull down your exports if it leaks.

Keys created before scopes existed keep all three — nothing broke when this shipped. Choose scopes when you create a key in Settings. A call missing a scope returns403 insufficient_scope, never 401: the key is valid, it just may not do that.

Rotation mints a replacement and leaves the old key working for a grace period (default 24h, max 168h), so you can deploy the new one without downtime. The replacement inherits the original's scopes.

Rotation is a signed-in account action — an API key cannot rotate itself, or a leaked key could mint a fresh one and survive revocation. If a key may have leaked,revokeit instead: revocation is immediate and has no grace period.

/api/openapi.json— OpenAPI 3.1, generated from the same constants the API enforces, so it cannot drift into describing something we do not do.

npx openapi-typescript https://furlen.pro/api/openapi.json -o furlen.d.ts

There is no Furlen SDK package — the API is two endpoints, and a dependency you have to trust is a poor trade for the ~20 lines below. Both examples poll, because Furlen has no webhooks (see below).

// Node 18+ — no dependencies. const KEY = process.env.FURLEN_API_KEY; const h = { authorization: \Bearer ${KEY}\, 'content-type': 'application/json' }; const start = await fetch('https://furlen.pro/api/v1/renders', { method: 'POST', headers: h, body: JSON.stringify({ csv: 'month,revenue\n2024-01,100\n2024-02,250' }), }); if (!start.ok) throw new Error(\${start.status}: ${(await start.json()).code}\); const { renderId } = await start.json(); // Poll. 60/min is the ceiling, so ~2s is comfortable. // On 429, WAIT for Retry-After (plus jitter) — a bare \continue\ would hammer the limiter. let attempts = 0; for (;;) { await new Promise((r) => setTimeout(r, 2000)); const res = await fetch(\https://furlen.pro/api/v1/renders/${renderId}\, { headers: h }); if (res.status === 429) { if (++attempts > 10) throw new Error('rate limited too long — giving up'); const wait = Number(res.headers.get('retry-after') ?? 5)  1000 + Math.random()  500; await new Promise((r) => setTimeout(r, wait)); continue; } attempts = 0; if (!res.ok) throw new Error(\polling failed: ${res.status}\); const job = await res.json(); if (job.status === 'completed') { console.log(job.downloadUrl); break; } if (job.status === 'failed') throw new Error(job.error); if (job.status === 'cancelled') throw new Error('render cancelled'); }
# Python 3.9+ — pip install requests import os, time, requests KEY = os.environ["FURLEN_API_KEY"] H = {"authorization": f"Bearer {KEY}"} r = requests.post("https://furlen.pro/api/v1/renders", headers=H, json={"csv": "month,revenue\n2024-01,100\n2024-02,250"}) r.raise_for_status() render_id = r.json()["renderId"] while True: time.sleep(2) res = requests.get(f"https://furlen.pro/api/v1/renders/{render_id}", headers=H) if res.status_code == 429: time.sleep(int(res.headers.get("Retry-After", 5))) continue res.raise_for_status() job = res.json() if job["status"] == "completed": print(job["downloadUrl"]) break if job["status"] == "failed": raise RuntimeError(job["error"]) if job["status"] == "cancelled": raise RuntimeError("render cancelled")

Downloading the finished export needs a key withexports:read.

- No webhooks.Renders are polled — the loops above are the supported pattern. If callbacks would change your design, emailsupport@furlen.pro.
- No sandbox or test mode.Every key issg_live_, and every render is a real render against your quota. Test on the free plan — previews are unlimited and watermarked.
- No published SDK.Copy the examples above, or generate a typed client from the OpenAPI spec.
- No status page./api/healthanswers{ status: "ok" }to anyone and returns the config probe — wired services and warnings — only to a request carrying an API key. Either way it is a reachability check, not uptime history.

Is there an MCP server for making charts?

Yes.furlen-mcpis published to the official Model Context Protocol registry asio.github.amitsha86/furlen-mcpand to npm. It gives Claude, Cursor, or any MCP client three tools that turn a table of numbers into an animated chart video — and it recomputes every figure in the narration against your rows before it will export anything. The server is open source:github.com/amitsha86/furlen-mcp.

Three things.furlen_public_data(from v0.1.2) takes a plain-English request — “India GDP over the last 10 years”, “compare US and China population”, “Apple revenue over 6 years” — and returns real rows from the World Bank, IMF, FRED, Eurostat, UN Comtrade, WHO or company filings, with a ready-madecsvstring and provenance naming the exact indicator.furlen_rendertakes CSV — that one, or the agent’s own — and runs the whole pipeline: profile, find the insights, write the story, verify every claim, render. It returns arenderId; you can set the audience, aspect ratio (16:9, 9:16 or 1:1), mp4 or gif, and resolution. Thenfurlen_render_statuspolls that id for a download URL. Renders usually take 30–90 seconds, so poll rather than block.

Can an agent chart World Bank data without a spreadsheet?

Yes — that is whatfurlen_public_datais for, and it is also a plain HTTP endpoint if you would rather not use MCP:POST /api/v1/datawith{ "prompt": "…" }and thedata:readscope. It resolves the request through the same adapters the studio uses, so it inherits the same refusals: ask for something no connected source publishes and you get a 422 withcode: "subject_unsupported"naming the subject, not a plausible-looking chart. Rate limited to 6 requests a minute — lower than the render endpoint, because each call fans out to statistical agencies that rate-limit us in turn.

curl -X POST https://furlen.pro/api/v1/data \ -H "Authorization: Bearer sg_live_..." \ -H "Content-Type: application/json" \ -d '{"prompt": "India GDP over the last 10 years"}'

The same thing it means in the app, because it is the same server-side gate. Every numeric claim the AI writes is recomputed from the rows you passed in, and a claim that doesn’t reconcile blocks the export instead of shipping. That matters more through an agent than through a UI: nobody is watching the intermediate output, so the check has to be the thing that refuses rather than a human noticing. You can watch it happen onthe proof page.

Create an API key in Settings, then add the block below toclaude_desktop_config.json,.cursor/mcp.json, or your client’s equivalent. It is configuration only — your AI tool starts the connector itself, and nothing is added to your computer.

{ "mcpServers": { "furlen": { "command": "npx", "args": ["-y", "furlen-mcp"], "env": { "FURLEN_API_KEY": "sg_live_..." } } } }

It can’t upload a file — data arrives as CSV text in the request or by naming a public dataset, not as a spreadsheet attachment. Rendering is asynchronous, so there is no “give me a chart back now” call. And the sources are the sources: if no connected provider publishes the figure, the answer is a refusal rather than an estimate. It needs an API key with the right scopes, and your plan’s limits on resolution, watermarking and render minutes apply exactly as they do in the app. MIT licensed.

AI Video, Image & Audio Generation with over 150 models

Create, edit, and export SVGator animations (SVG, Lottie, GIF, video) from your AI assistant, and keep an editable project you can open in the editor anytime.

You're agent can Chain 60+ AI image and video models on one workflow canvas

Hosted remote MCP server for AI image and video generation from one Streamable HTTP endpoint.

Generate and edit on-brand images, video, audio, and 3D from any MCP-compatible AI assistant.

fficial remote MCP server for RunComfy's Serverless API (ComfyUI): manage GPU deployments and run async image/video inference.

Control Adobe After Effects through a standardized protocol, enabling AI assistants and other applications.

An MCP server that allows AI assistants to interact with Adobe After Effects.

A lightweight MCP (Model Context Protocol) server for Blender. It offers a natural language interface with Blender’s Python API, improving access to documentation, and allowing users to explore and understand complex setups.

Create AI music videos and audio-reactive visuals from songs through MCP.

No reviews yet — be the first

Sign in to leave a review

Use Google, GitHub, or an email account so ratings stay tied to real people.

Email sign in

No reviews posted yet.