AI Agents & LLMs
Chartly is designed to be easy for AI agents to call. The API is a single HTTP request that takes a Chart.js JSON config and returns a permanent image URL — perfect for LLMs that need to render charts inside chat replies, reports, or generated documents.
TL;DR for AI agents
- Endpoint:
POST https://api.chartly.dev/v1/chart/create - Auth: header
X-Api-Key: <your_key> - Body:
{ "chart": <chart.js config>, "width": <1-2000>, "height": <1-2000>, "format": "png" | "svg" } - Response:
{ "url": "https://api.chartly.dev/v1/chart/<id>" }— a permanent, cacheable image URL safe to embed in messages, markdown, or HTML
Use /v1/chart/create (not /v1/chart) from agents — it returns a URL instead of binary bytes, which is what LLMs want to put into their final answer.
LLM-friendly resources
Chartly publishes machine-readable indexes designed for LLMs and AI crawlers:
- llms.txt — Site map of curated documentation, following the llmstxt.org convention
- llms-full.txt — All documentation concatenated into a single plain-text file for one-shot ingestion
- OpenAPI JSON — Machine-readable API schema
- OpenAPI YAML — Same schema in YAML
Point your agent or RAG indexer at these instead of crawling the rendered HTML.
Tool definition: Anthropic SDK (Claude)
Drop this directly into your tools array when calling the Claude API:
{
"name": "chartly_create_chart",
"description": "Render a chart from a Chart.js configuration and return a permanent image URL. Use this whenever the user asks for a chart, graph, or visualization. The returned URL is a PNG (or SVG) that can be embedded in markdown like  or HTML <img src=\"url\">.",
"input_schema": {
"type": "object",
"required": ["chart", "width", "height"],
"properties": {
"chart": {
"type": "object",
"description": "A complete Chart.js 4.4 configuration object with `type`, `data`, and `options`. Always set options.responsive to false."
},
"width": {
"type": "integer",
"minimum": 1,
"maximum": 2000,
"description": "Image width in pixels."
},
"height": {
"type": "integer",
"minimum": 1,
"maximum": 2000,
"description": "Image height in pixels."
},
"format": {
"type": "string",
"enum": ["png", "svg"],
"default": "png"
},
"backgroundColor": {
"type": "string",
"description": "CSS color string or 'transparent'."
}
}
}
}
Minimal handler in Python:
import os, requests
from anthropic import Anthropic
client = Anthropic()
def chartly_create_chart(args):
r = requests.post(
"https://api.chartly.dev/v1/chart/create",
headers={"X-Api-Key": os.environ["CHARTLY_API_KEY"]},
json=args,
timeout=30,
)
r.raise_for_status()
return r.json()["url"] # e.g. https://api.chartly.dev/v1/chart/abc123
# When Claude requests this tool, return the URL string as the tool result.
Tool definition: OpenAI / function calling
{
"type": "function",
"function": {
"name": "chartly_create_chart",
"description": "Render a chart from a Chart.js configuration and return a permanent image URL.",
"parameters": {
"type": "object",
"required": ["chart", "width", "height"],
"properties": {
"chart": { "type": "object" },
"width": { "type": "integer", "minimum": 1, "maximum": 2000 },
"height": { "type": "integer", "minimum": 1, "maximum": 2000 },
"format": { "type": "string", "enum": ["png", "svg"] },
"backgroundColor": { "type": "string" }
}
}
}
}
The handler is identical — call POST /v1/chart/create and return the resulting URL.
End-to-end example: Claude agent
import os, json, requests
from anthropic import Anthropic
client = Anthropic()
TOOLS = [{
"name": "chartly_create_chart",
"description": "Render a Chart.js config to a permanent image URL.",
"input_schema": {
"type": "object",
"required": ["chart", "width", "height"],
"properties": {
"chart": {"type": "object"},
"width": {"type": "integer"},
"height": {"type": "integer"},
"format": {"type": "string", "enum": ["png", "svg"]},
},
},
}]
def chartly_create_chart(args):
r = requests.post(
"https://api.chartly.dev/v1/chart/create",
headers={"X-Api-Key": os.environ["CHARTLY_API_KEY"]},
json=args, timeout=30,
)
r.raise_for_status()
return r.json()["url"]
messages = [{
"role": "user",
"content": "Plot Q1-Q4 revenue: 12, 19, 15, 25 (in $k). Use a bar chart.",
}]
while True:
resp = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=2048,
tools=TOOLS,
messages=messages,
)
if resp.stop_reason == "tool_use":
tool_use = next(b for b in resp.content if b.type == "tool_use")
result = chartly_create_chart(tool_use.input)
messages.append({"role": "assistant", "content": resp.content})
messages.append({
"role": "user",
"content": [{
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": result,
}],
})
continue
print(next(b.text for b in resp.content if b.type == "text"))
break
Prompting tips
- Tell the model: "Always set
options.responsive: falsein chart configs." Chart.js responsive mode produces blank images at fixed dimensions. - Tell the model: "Use
chartly_create_chartwhenever the user asks for a chart, graph, or visualization." - Embed the returned URL with markdown:
. The image is cached and CDN-served, so it loads fast in chat UIs. - For one-off in-memory rendering (no public URL), use
POST /v1/chartinstead — it returns raw image bytes.
Robots and crawler policy
docs.chartly.dev allows GPTBot, ClaudeBot, PerplexityBot, Google-Extended, and other reputable AI crawlers by default — see the published robots.txt. The API itself (api.chartly.dev) does not need to be crawled; agents should call it directly.
Need a different shape?
- For a quick demo without an account, see Trial API keys.
- For permanent shareable URLs without exposing your key, see Signed URLs.
- For chart styling examples by type, see Chart Types.