# Open Graph Image Generator (Social Cards) (`kelp_forest_labs/og-image-generator`) Actor

Make clean Open Graph and social share images from text: title, subtitle, label, footer and your logo, in three templates and your brand colours. 1200x630 by default or any size up to 2400 px, PNG or JPEG, one image per card in bulk. Invalid cards are free.

- **URL**: https://apify.com/kelp_forest_labs/og-image-generator.md
- **Developed by:** [kelp forest](https://apify.com/kelp_forest_labs) (community)
- **Categories:** Developer tools, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 images

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

Turn a title (plus optional subtitle, label, footer and logo) into a clean **Open Graph / social card image**.
Send one card or thousands; every image uses your brand colours.

### Use cases

- Share images for every blog post or docs page from a CSV of titles.
- Event, release or podcast episode cards in a consistent brand style.
- Cover images for newsletters and changelogs.
- Automated cards from a CMS or an AI agent pipeline.

### What it does

- Three templates: `classic`, `split` and `minimal`.
- Hex brand colours for background, text and accent; PNG or JPEG; any size from 200 to 2400 px, for example
  1200×630 (Open Graph), 1080×1080 (square), 1600×900 (16:9) or 1500×300 / 1500×500 (banners).
- Text always fits: long titles wrap and every element (label, title, subtitle, footer, logo) is scaled down
  together until nothing is cut off. If that would need a title below 12 px, the card is a free error row instead
  of an image with clipped text. The label, subtitle and footer never go below 9 px. Each row reports `textScale`,
  `titleFontPx` and `smallestTextPx`.
- All three templates show the label (`eyebrow`) and `footer` when you set them.
- Bulk mode: a `cards` list, each card can override template and colours.
- All text is HTML-escaped. The only network request a card may make is to its own vetted logo URL.

### Pricing (pay per event)

| Event | Price |
|---|---|
| `image` (one image saved) | US$0.002 |

**Invalid cards are free:** a missing title, a colour that is not hex, an unknown template, or a logo that is blocked or cannot be loaded gives
an error row and no charge.

Each run also has Apify's run-start charge of US$0.00005 per GB of run memory (US$0.0001 at the default 2048 MB),
charged when the run starts, even if every card is invalid.

### How to use

1. Open the actor in Apify Console and fill in the input form (or paste the JSON example below into the JSON editor).
2. Optional: set a maximum charge for the run under the run options to cap what a run can cost.
3. Click **Start**. Results appear in the **Output** tab as they are produced; export them as JSON, CSV, Excel or HTML.

You can also start runs from the Apify API or CLI, run the actor on a schedule (for example weekly), and send
results to other apps with Apify integrations and webhooks.

### Input example

```json
{"cards": [{"title": "How we test spreadsheets", "eyebrow": "Blog", "footer": "example.com"}], "template": "split", "accent": "#e0b04a"}
```

### Output example (shape; values vary)

```json
{"index": 0, "title": "How we test spreadsheets", "status": "ok", "key": "og-0000-how-we-test-spreadsheets-3f2a1b.png", "imageUrl": "https://api.apify.com/v2/key-value-stores/.../records/og-0000-...png", "width": 1200, "height": 630, "template": "split", "bytes": "<size>"}
```

### FAQ

**Does it use AI to create pictures?**
No. It lays out your text, colours and logo in a template, so results are predictable and text is always spelled exactly as you wrote it.

**Which fonts and languages work?**
The container fonts (DejaVu and Liberation) cover Latin, Greek and Cyrillic. Emoji and CJK text may not render.

**What if my logo URL does not work?**
That card gets a free error row instead of a card without a logo. The message says what went wrong: the HTTP status (for example 404), a response that is not an image (such as an error page or HTML), a request that failed or timed out, or an address that is private or internal.

**Can I use my own size?**
Yes: `width` and `height` from 200 to 2400 px, for example 1200x630 (Open Graph), 1080x1080 (square posts), 1600x900 (16:9) or 1500x500 (banners).

**What if my text is too long for the size?**
Text is wrapped and scaled down until every element (label, title, subtitle, footer, logo) fits inside the image. If it would need a title smaller than 12 px, you get a free error row instead of a card with clipped text. The smaller text (label, subtitle, footer) never goes below 9 px; each row reports `smallestTextPx`.

### Limits: what this actor does not do

- It does not generate pictures with AI or fetch stock photos. It lays out your text and your logo.
- It does not load fonts or scripts from the internet; it uses the fonts in the container (DejaVu/Liberation).
- The logo is the only remote file it loads. A logo URL on, or redirecting to, a private/internal address is refused
  (free error row for that card). Logo URLs are normalised first (spaces, non-ASCII characters, upper-case
  scheme or host, dot segments in the path; a `#fragment` is dropped), so the URL that is checked is the one that
  is loaded.

### Changelog

- 1.0 (October 2026): first public version.

# Actor input Schema

## `title` (type: `string`):

Quick mode: one card with this title.

## `subtitle` (type: `string`):

Optional second line.

## `eyebrow` (type: `string`):

Small label above the title, e.g. a category.

## `footer` (type: `string`):

Small text at the bottom, e.g. your domain.

## `logoUrl` (type: `string`):

Public http(s) image URL. No other network requests are made.

## `cards` (type: `array`):

List of objects with title, subtitle, eyebrow, footer, logoUrl and optional template/background/foreground/accent overrides.

## `template` (type: `string`):

Layout used when a card does not set its own.

## `background` (type: `string`):

Hex colour.

## `foreground` (type: `string`):

Hex colour.

## `accent` (type: `string`):

Hex colour.

## `width` (type: `integer`):

Image width in px (200-2400). Common sizes: 1200×630 Open Graph (default), 1080×1080 square post, 1600×900 16:9, 1500×500 or 1500×300 banner.

## `height` (type: `integer`):

Image height in px (200-2400). Text is scaled down to fit; if it would need a title under 12 px, the card is a free error row.

## `format` (type: `string`):

Output image format.

## Actor input object example

```json
{
  "title": "Small, reliable tools for developers",
  "cards": [
    {
      "title": "How we test spreadsheets before selling them",
      "eyebrow": "Blog"
    }
  ],
  "template": "classic",
  "background": "#0b2545",
  "foreground": "#ffffff",
  "accent": "#e0b04a",
  "width": 1200,
  "height": 630,
  "format": "png"
}
```

# Actor output Schema

## `results` (type: `string`):

One row per card with the key and URL of the saved image, or a free error row.

## `files` (type: `string`):

Every generated PNG or JPEG image in the run's key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "title": "Small, reliable tools for developers",
    "cards": [
        {
            "title": "How we test spreadsheets before selling them",
            "eyebrow": "Blog"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kelp_forest_labs/og-image-generator").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "title": "Small, reliable tools for developers",
    "cards": [{
            "title": "How we test spreadsheets before selling them",
            "eyebrow": "Blog",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("kelp_forest_labs/og-image-generator").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "title": "Small, reliable tools for developers",
  "cards": [
    {
      "title": "How we test spreadsheets before selling them",
      "eyebrow": "Blog"
    }
  ]
}' |
apify call kelp_forest_labs/og-image-generator --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,kelp_forest_labs/og-image-generator"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/zfRI73ybIOqcJiThH/builds/YRnCcw1A69h04gQ9l/openapi.json
