Go Image Generation API: Complete Guide

Go Image Generation API: Complete Guide

No image generation API has published a Go integration guide. Not Bannerbear, not Placid, not anyone. If you've been searching for "golang image generation API," you probably found nothing useful. We have guides for Node.js, Python, and PHP/Laravel too, but this one is all Go.

That changes now. Go's standard library makes this almost embarrassingly simple. No SDKs to install, no dependencies to manage. Just net/http, a JSON payload, and you're generating images. Here's the complete guide with production-ready code. All pricing as of March 2026.

Basic image generation in goBasic Image Generation in Go

Here's the simplest working example. I've stripped it down to the essentials:

package main
 
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)
 
func main() {
	templateID := "YOUR_TEMPLATE_ID"
	apiKey := os.Getenv("IMEJIS_API_KEY")
 
	// Build the JSON payload
	payload := map[string]any{
		"headline": map[string]any{
			"text": "Hello from Go!",
		},
		"subtitle": map[string]any{
			"text": "Generated with net/http",
		},
	}
 
	body, _ := json.Marshal(payload)
 
	// Make the API call
	url := fmt.Sprintf("https://render.imejis.io/v1/%s", templateID)
	req, _ := http.NewRequest("POST", url, bytes.NewReader(body))
	req.Header.Set("dma-api-key", apiKey)
	req.Header.Set("Content-Type", "application/json")
 
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Request failed: %v\n", err)
		os.Exit(1)
	}
	defer resp.Body.Close()
 
	// Save the image
	imageData, _ := io.ReadAll(resp.Body)
	os.WriteFile("output.png", imageData, 0644)
	fmt.Println("Image saved to output.png")
}

Run it: IMEJIS_API_KEY=your-key go run main.go

That's it. No external packages. The API returns the image binary directly, so you just write it to a file.

Production ready clientProduction-Ready Client

The basic example works, but production code needs proper error handling, timeouts, and reusable structure. Here's what I use in real projects:

package imejis
 
import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"
)
 
// Client handles image generation API calls.
type Client struct {
	apiKey     string
	baseURL    string
	httpClient *http.Client
}
 
// NewClient creates a configured API client.
func NewClient(apiKey string) *Client {
	return &Client{
		apiKey:  apiKey,
		baseURL: "https://render.imejis.io/v1",
		httpClient: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}
 
// Generate renders an image from a template with the given data.
func (c *Client) Generate(ctx context.Context, templateID string, data map[string]any) ([]byte, error) {
	body, err := json.Marshal(data)
	if err != nil {
		return nil, fmt.Errorf("marshal payload: %w", err)
	}
 
	url := fmt.Sprintf("%s/%s", c.baseURL, templateID)
	req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewReader(body))
	if err != nil {
		return nil, fmt.Errorf("create request: %w", err)
	}
 
	req.Header.Set("dma-api-key", c.apiKey)
	req.Header.Set("Content-Type", "application/json")
 
	resp, err := c.httpClient.Do(req)
	if err != nil {
		return nil, fmt.Errorf("api request: %w", err)
	}
	defer resp.Body.Close()
 
	if resp.StatusCode != http.StatusOK {
		errBody, _ := io.ReadAll(resp.Body)
		return nil, fmt.Errorf("api error %d: %s", resp.StatusCode, string(errBody))
	}
 
	return io.ReadAll(resp.Body)
}

Using the clientUsing the Client

package main
 
import (
	"context"
	"fmt"
	"os"
	"your-module/imejis"
)
 
func main() {
	client := imejis.NewClient(os.Getenv("IMEJIS_API_KEY"))
 
	imageData, err := client.Generate(
		context.Background(),
		"YOUR_TEMPLATE_ID",
		map[string]any{
			"headline":      map[string]any{"text": "Summer Sale"},
			"price":         map[string]any{"text": "$29.99"},
			"product_image": map[string]any{"image": "https://example.com/product.jpg"},
		},
	)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Generation failed: %v\n", err)
		os.Exit(1)
	}
 
	os.WriteFile("output.png", imageData, 0644)
	fmt.Println("Done!")
}

Clean, testable, and handles errors properly. The context.Context parameter lets you cancel long-running requests or set deadlines.

Typed payloads with structsTyped Payloads with Structs

If you're generating the same template repeatedly, define a struct instead of using map[string]any:

type ProductCard struct {
	Headline     TextField `json:"headline"`
	Price        TextField `json:"price"`
	ProductImage ImageField `json:"product_image"`
	Badge        TextField `json:"discount_badge,omitempty"`
}
 
type TextField struct {
	Text  string `json:"text"`
	Color string `json:"color,omitempty"`
}
 
type ImageField struct {
	Image string `json:"image"`
}

Now your generation calls are type-safe:

card := ProductCard{
	Headline:     TextField{Text: "Classic Runner"},
	Price:        TextField{Text: "$29.99"},
	ProductImage: ImageField{Image: "https://example.com/runner.jpg"},
	Badge:        TextField{Text: "40% OFF"},
}
 
// Marshal card to JSON and pass to client.Generate()

The compiler catches typos. Your IDE gives you autocomplete. And omitempty on optional fields means they're excluded from the JSON when empty.

Concurrent generation with goroutinesConcurrent Generation with Goroutines

This is where Go really shines. Generating 100 images sequentially takes about 3 minutes. With goroutines, you can cut that to under 30 seconds.

package main
 
import (
	"context"
	"fmt"
	"os"
	"sync"
	"your-module/imejis"
)
 
type Product struct {
	ID    string
	Name  string
	Price string
	Image string
}
 
func main() {
	client := imejis.NewClient(os.Getenv("IMEJIS_API_KEY"))
	templateID := "YOUR_TEMPLATE_ID"
 
	products := []Product{
		{ID: "1", Name: "Classic Runner", Price: "$29.99", Image: "https://example.com/runner.jpg"},
		{ID: "2", Name: "Urban Jacket", Price: "$89.00", Image: "https://example.com/jacket.jpg"},
		// ... hundreds more
	}
 
	// Limit concurrency to 5 simultaneous requests
	sem := make(chan struct{}, 5)
	var wg sync.WaitGroup
 
	for _, p := range products {
		wg.Add(1)
		sem <- struct{}{} // acquire semaphore
 
		go func(product Product) {
			defer wg.Done()
			defer func() { <-sem }() // release semaphore
 
			data := map[string]any{
				"product_name":  map[string]any{"text": product.Name},
				"price":         map[string]any{"text": product.Price},
				"product_image": map[string]any{"image": product.Image},
			}
 
			imageData, err := client.Generate(context.Background(), templateID, data)
			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed %s: %v\n", product.ID, err)
				return
			}
 
			os.WriteFile(fmt.Sprintf("output/%s.png", product.ID), imageData, 0644)
			fmt.Printf("Generated: %s\n", product.Name)
		}(p)
	}
 
	wg.Wait()
	fmt.Println("All done!")
}

The semaphore channel limits concurrency to 5 simultaneous requests. This keeps you under rate limits while still being 5x faster than sequential generation. I've used this pattern to generate 2,000 product images in about 12 minutes. For more on managing rate limits and caching at scale, see image API rate limits and caching.

Http handler generate images on requestHTTP Handler: Generate Images on Request

If you're building a web service that generates images on the fly:

func handleGenerate(client *imejis.Client) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		title := r.URL.Query().Get("title")
		if title == "" {
			http.Error(w, "title parameter required", http.StatusBadRequest)
			return
		}
 
		imageData, err := client.Generate(r.Context(), "YOUR_TEMPLATE_ID", map[string]any{
			"headline": map[string]any{"text": title},
		})
		if err != nil {
			http.Error(w, "generation failed", http.StatusInternalServerError)
			return
		}
 
		w.Header().Set("Content-Type", "image/png")
		w.Header().Set("Cache-Control", "public, max-age=86400")
		w.Write(imageData)
	}
}

Now GET /generate?title=Hello+World returns a PNG image. Useful for dynamic OG images, email headers, or any endpoint that needs to serve generated images.

Common patternsCommon Patterns

Retry with backoffRetry with Backoff

func (c *Client) GenerateWithRetry(ctx context.Context, templateID string, data map[string]any) ([]byte, error) {
	var lastErr error
	for attempt := 0; attempt < 3; attempt++ {
		result, err := c.Generate(ctx, templateID, data)
		if err == nil {
			return result, nil
		}
		lastErr = err
		time.Sleep(time.Duration(attempt+1) * time.Second)
	}
	return nil, fmt.Errorf("failed after 3 attempts: %w", lastErr)
}

Upload to s3Upload to S3

func uploadToS3(bucket, key string, data []byte) error {
	cfg, _ := config.LoadDefaultConfig(context.Background())
	client := s3.NewFromConfig(cfg)
 
	_, err := client.PutObject(context.Background(), &s3.PutObjectInput{
		Bucket:      &bucket,
		Key:         &key,
		Body:        bytes.NewReader(data),
		ContentType: aws.String("image/png"),
	})
	return err
}

Why go works well for image generationWhy Go Works Well for Image Generation

I've written image generation code in Node.js, Python, and Go. Go has some real advantages for this use case:

  • Goroutines for concurrency: 5 lines of code to parallelize 1,000 image generations. Try doing that in Python without asyncio headaches.
  • No dependencies needed: The standard library handles HTTP, JSON, file I/O. Your go.mod stays clean.
  • Fast startup: No runtime warmup. Great for serverless functions or CLI tools.
  • Type safety with structs: Catch template field typos at compile time, not at runtime.
  • Single binary deployment: go build and deploy one file. No node_modules, no virtualenvs.

The only downside? No official SDK from any image API provider. But as you've seen, you don't need one. Go's standard library is the SDK.

Getting startedGetting Started

  1. Sign up for Imejis.io: 100 free images per month
  2. Create a template in the visual editor
  3. Copy the basic example from this guide
  4. Set your API key: export IMEJIS_API_KEY=your-key
  5. Run: go run main.go

First image in under 5 minutes. No dependencies to install. If you're new to image APIs in general, our beginner guide to generating images with an API covers the fundamentals.

For the JSON payload format, see our Generate Images from JSON guide. For bulk generation, check Batch Image Generation from CSV. And for API pricing, see our Image Generation API Pricing Comparison.

FaqFAQ

Can i generate images from a go applicationCan I generate images from a Go application?

Yes. Use Go's standard net/http package to call the API. Send a POST with JSON, get image bytes back. No external libraries needed.

Do i need an sdk for go image generationDo I need an SDK for Go image generation?

No. Go's standard library handles HTTP and JSON natively. The code in this guide uses only net/http, encoding/json, and os. Zero third-party dependencies.

How fast is image generation from goHow fast is image generation from Go?

The API call takes under 2 seconds. Go's goroutines make concurrent generation trivial. With 5 goroutines, you can generate about 150 images per minute.

What image formats does the api returnWhat image formats does the API return?

Imejis.io returns PNG by default. The response body is raw image bytes that you write to a file, upload to S3, or serve over HTTP.

How much does image generation cost from goHow much does image generation cost from Go?

Same as any language. Imejis.io starts at $14.99/month for 1,000 images, with a free tier of 100/month. The API doesn't charge differently based on which language calls it.

Go generate some imagesGo Generate Some Images

Go's strength is doing simple things fast with minimal dependencies. Image generation via API fits that perfectly. Try Imejis.io free and generate your first image from Go in about 5 minutes.