general9 min read

Publication-Quality Charts via API: No Matplotlib Required

Generate publication-quality charts from data via API. No matplotlib, no config, no visualization code. Upload CSV, get dark-themed PNGs back.

By DataStoryBot Team

Publication-Quality Charts via API: No Matplotlib Required

You are building an application that needs charts. Maybe it is a reporting tool, a client portal, a Slack bot that posts weekly metrics, or a data pipeline that generates PDF summaries. You need to turn data into images — bar charts, line charts, scatter plots — and you need them to look good enough to put in front of stakeholders.

The traditional approach: add matplotlib to your dependencies, write visualization code, maintain style configs, debug rendering issues across environments, and accept that chart generation is now a permanent part of your codebase. For many applications, this is overkill. You do not need a visualization library. You need a visualization service.

This article shows how to generate publication-quality charts from CSV data using the DataStoryBot API, with no visualization code on your end.

The Matplotlib Tax

Here is what generating a single polished chart looks like with matplotlib:

import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker

df = pd.read_csv("revenue_by_region.csv")

fig, ax = plt.subplots(figsize=(10, 6), dpi=150)
plt.style.use("dark_background")
fig.patch.set_facecolor("#141414")
ax.set_facecolor("#141414")

pivot = df.pivot_table(values="revenue", index="quarter", columns="region",
                       aggfunc="sum")
pivot.plot(kind="bar", ax=ax, width=0.8, colormap="Set2")

ax.set_title("Quarterly Revenue by Region", fontsize=16, color="#ffffff",
             fontweight="bold", pad=15)
ax.set_xlabel("")
ax.set_ylabel("Revenue", fontsize=12, color="#cccccc")
ax.yaxis.set_major_formatter(mticker.FuncFormatter(
    lambda x, p: f"${x/1e6:.1f}M"))
ax.tick_params(colors="#999999", rotation=0)
ax.legend(title="Region", framealpha=0.3, fontsize=10)

for spine in ax.spines.values():
    spine.set_color("#333333")

plt.tight_layout()
plt.savefig("revenue_chart.png", transparent=True, bbox_inches="tight")
plt.close()

That is 28 lines for one chart. A typical analysis needs three or four. And this does not account for the decisions you had to make before writing a single line: Should this be a bar chart or a line chart? Grouped or stacked? What color palette works on dark backgrounds? How should the y-axis format large numbers?

Multiply this across every dataset your application processes, and you have a significant maintenance burden that has nothing to do with your core product.

The API Alternative

With DataStoryBot, chart generation is a side effect of data analysis. You upload a CSV, the platform analyzes the data inside an ephemeral Code Interpreter container running GPT-4o, and charts come back as downloadable PNGs. The AI handles chart type selection, axis formatting, color palettes, and layout.

Here is the complete flow:

Step 1: Upload Data

curl -X POST https://datastory.bot/api/upload \
  -F "file=@revenue_data.csv"
{
  "containerId": "ctr_x7y8z9",
  "fileId": "file-upl001",
  "metadata": {
    "fileName": "revenue_data.csv",
    "rowCount": 3200,
    "columnCount": 7,
    "columns": ["date", "region", "product", "revenue", "units", "cost", "channel"]
  }
}

Step 2: Analyze

curl -X POST https://datastory.bot/api/analyze \
  -H "Content-Type: application/json" \
  -d '{"containerId": "ctr_x7y8z9"}'

The analysis returns three story angles. Each story implies a set of visualizations that will be generated in the next step.

Step 3: Refine and Get Charts

curl -X POST https://datastory.bot/api/refine \
  -H "Content-Type: application/json" \
  -d '{
    "containerId": "ctr_x7y8z9",
    "selectedStoryTitle": "Western Region Drives 60% of Growth Despite Flat National Trend"
  }'
{
  "narrative": "## Western Region Drives 60% of Growth...\n\nWhile national revenue grew just 4% year-over-year...",
  "charts": [
    {
      "fileId": "file-chart201",
      "caption": "Year-over-year revenue growth rate by region"
    },
    {
      "fileId": "file-chart202",
      "caption": "Western region revenue by product category, Q1-Q4 2025"
    },
    {
      "fileId": "file-chart203",
      "caption": "Regional revenue share trend showing Western region expansion"
    }
  ],
  "resultDataset": {
    "fileId": "file-ds201",
    "caption": "Filtered dataset: regional revenue with YoY growth calculations"
  }
}

Step 4: Download the PNGs

curl -o growth_by_region.png \
  "https://datastory.bot/api/files/ctr_x7y8z9/file-chart201"

curl -o western_categories.png \
  "https://datastory.bot/api/files/ctr_x7y8z9/file-chart202"

curl -o share_trend.png \
  "https://datastory.bot/api/files/ctr_x7y8z9/file-chart203"

Each PNG is ready to use. No post-processing. No format conversion. No background color fixes.

Why Dark Theme by Default

DataStoryBot charts use a dark theme — #141414 background, light text, muted gridlines. This is a deliberate design choice, not an aesthetic preference.

Dark backgrounds work in more contexts. Light charts on a dark dashboard look broken. Dark charts on a light page look intentional. Transparent PNGs with dark styling embed cleanly in slide decks, dark-mode UIs, email templates, and PDF reports. If you set the container background in your target medium, the chart integrates seamlessly.

Reduced visual noise. Dark backgrounds suppress the "chart in a white box" look that makes automated reports feel generated. The charts look like they belong in a designed interface rather than a Jupyter notebook.

Readability. High-contrast text and data elements on dark backgrounds are easier to scan in low-light environments where a lot of data review actually happens — late-night dashboards, dimmed conference rooms, mobile screens at full brightness.

If you need a different style for a specific use case, the refinementPrompt parameter lets you request it:

curl -X POST https://datastory.bot/api/refine \
  -H "Content-Type: application/json" \
  -d '{
    "containerId": "ctr_x7y8z9",
    "selectedStoryTitle": "Western Region Drives 60% of Growth Despite Flat National Trend",
    "refinementPrompt": "Use a light background theme suitable for print"
  }'

Effort Comparison: Manual vs. API

Here is what the same three-chart output looks like with each approach:

Manual (matplotlib)API (DataStoryBot)
Lines of code80-12010-15
Dependenciesmatplotlib, pandas, seaborn, numpyrequests
Chart type decisionsYou decideAI decides based on data shape
Style configurationYou configureHandled automatically
Time to first chart15-30 min~40 seconds
MaintenanceOngoingNone — stateless API
Works on new schemasNeed new code per schemaSame three calls for any CSV

The API approach trades control for speed. If you need a very specific chart layout — custom annotations, dual y-axes with specific break points, branded color palettes that must match hex values exactly — matplotlib is the right tool. If you need "good charts from data, programmatically, for every CSV my users upload," the API is faster by an order of magnitude.

Python Integration Example

A complete function that takes a CSV path and returns chart file paths:

import requests
from pathlib import Path

BASE_URL = "https://datastory.bot"

def generate_charts(csv_path: str, output_dir: str = ".") -> list[str]:
    """Upload a CSV and return paths to generated chart PNGs."""
    output = Path(output_dir)
    output.mkdir(exist_ok=True)

    # Upload
    with open(csv_path, "rb") as f:
        upload = requests.post(
            f"{BASE_URL}/api/upload",
            files={"file": (Path(csv_path).name, f, "text/csv")}
        ).json()

    cid = upload["containerId"]

    # Analyze — get story angles
    stories = requests.post(
        f"{BASE_URL}/api/analyze",
        json={"containerId": cid}
    ).json()

    # Refine the first story to generate charts
    result = requests.post(
        f"{BASE_URL}/api/refine",
        json={
            "containerId": cid,
            "selectedStoryTitle": stories["stories"][0]["title"]
        }
    ).json()

    # Download all chart PNGs
    chart_paths = []
    for i, chart in enumerate(result["charts"]):
        resp = requests.get(f"{BASE_URL}/api/files/{cid}/{chart['fileId']}")
        path = output / f"chart_{i+1}.png"
        path.write_bytes(resp.content)
        chart_paths.append(str(path))

    return chart_paths

Usage:

charts = generate_charts("quarterly_metrics.csv", output_dir="./reports")
# ['./reports/chart_1.png', './reports/chart_2.png', './reports/chart_3.png']

That function works for any CSV. No schema-specific code. No chart type logic. No style configuration. Pass a sales dataset, get sales charts. Pass a server metrics export, get performance charts. The AI adapts.

When to Use a Chart API vs. a Charting Library

Use the API when:

  • Your application processes user-uploaded data with unpredictable schemas
  • You want charts without adding visualization dependencies to your stack
  • Consistent, professional styling matters more than pixel-level control
  • You are building reports, summaries, or notifications that need embedded images
  • You want chart type selection to be automatic, not hardcoded

Use matplotlib/plotly/D3 directly when:

  • You need interactive charts (zoom, hover, filter) — DataStoryBot produces static PNGs
  • Your chart design must match a specific brand guide to the hex code
  • You are building a real-time dashboard that updates on sub-second intervals
  • The visualization logic is your core product, not a supporting feature

For most backend applications that need "charts from data," the API approach eliminates an entire category of code you would otherwise have to write and maintain.

Chart Types the API Generates

The AI selects chart types dynamically based on data shape and story angle. There is no fixed chart menu. Code Interpreter examines the data and writes the appropriate matplotlib code inside the container.

Common outputs include:

Bar charts — for categorical comparisons. Revenue by product, counts by region, rankings. The AI chooses grouped or stacked variants based on the number of dimensions.

Line charts — for time series. Monthly trends, growth trajectories, moving averages. Often includes confidence bands or annotations for key inflection points.

Scatter plots — for relationships between continuous variables. Correlation stories, outlier detection, cluster identification. Frequently color-coded by a categorical dimension.

Heatmaps — for matrix data. Correlation matrices, pivot table visualizations, temporal patterns like day-of-week activity maps.

Distribution plots — histograms, box plots, violin plots. For understanding spread, skew, and group comparisons.

If the data has 50 categories, the AI will not try to render all of them in a single bar chart. It aggregates, filters, or switches chart types entirely. This is the kind of judgment that makes automated chart generation actually useful rather than a source of unreadable clutter.

For a detailed breakdown of chart type selection logic and all supported visualizations, see How to Generate Charts from CSV Data Automatically.

Chart Specifications

All charts generated by the API share these properties:

PropertyValue
FormatPNG with transparent background
Resolution150 DPI
Canvas10 x 6 inches (1500 x 900 px)
BackgroundDark theme (#141414)
TextWhite/light gray

These specs are consistent across every analysis. You can build your application layout around them without worrying about size variance.

Try It

Upload a CSV in the DataStoryBot playground and look at the charts that come back. If the quality meets your bar, the API gives you the same output programmatically.

For the full API setup and authentication details, start with Getting Started with the DataStoryBot API. Three endpoints, no visualization code, publication-quality charts.

Ready to find your data story?

Upload a CSV and DataStoryBot will uncover the narrative in seconds.

Try DataStoryBot →