← toolkit.bot

PDF to EPUB Conversion in Go — toolkit.bot API Integration

June 12, 2026  ·  8 min read

This guide shows how to call the toolkit.bot REST API from Go to convert PDF files to EPUB. Go's standard net/http package handles everything — no external dependencies needed.

Complete Go example

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
	"path/filepath"
	"time"
)

const apiBase = "https://toolkit.bot/api/v1"

type jobResponse struct {
	JobID       string `json:"job_id"`
	Status      string `json:"status"`
	DownloadURL string `json:"download_url"`
}

func convertPDFToEPUB(pdfPath, outputPath string) error {
	apiKey := os.Getenv("TOOLKIT_API_KEY")
	if apiKey == "" {
		return fmt.Errorf("TOOLKIT_API_KEY not set")
	}

	// --- Step 1: Upload ---
	f, err := os.Open(pdfPath)
	if err != nil {
		return fmt.Errorf("open pdf: %w", err)
	}
	defer f.Close()

	var buf bytes.Buffer
	mw := multipart.NewWriter(&buf)
	part, err := mw.CreateFormFile("file", filepath.Base(pdfPath))
	if err != nil {
		return err
	}
	if _, err = io.Copy(part, f); err != nil {
		return err
	}
	mw.Close()

	req, _ := http.NewRequest("POST", apiBase+"/jobs", &buf)
	req.Header.Set("Authorization", "Bearer "+apiKey)
	req.Header.Set("Content-Type", mw.FormDataContentType())

	client := &http.Client{Timeout: 60 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return fmt.Errorf("upload: %w", err)
	}
	defer resp.Body.Close()

	var upload jobResponse
	if err = json.NewDecoder(resp.Body).Decode(&upload); err != nil {
		return fmt.Errorf("decode upload response: %w", err)
	}
	fmt.Printf("Job ID: %s
", upload.JobID)

	// --- Step 2: Poll ---
	var dlURL string
	for i := 0; i < 60; i++ {
		time.Sleep(4 * time.Second)
		req, _ = http.NewRequest("GET", apiBase+"/jobs/"+upload.JobID, nil)
		req.Header.Set("Authorization", "Bearer "+apiKey)
		resp, err = client.Do(req)
		if err != nil {
			return err
		}
		var status jobResponse
		json.NewDecoder(resp.Body).Decode(&status)
		resp.Body.Close()
		fmt.Printf("Status [%d]: %s
", i+1, status.Status)
		if status.Status == "done" {
			dlURL = status.DownloadURL
			break
		}
		if status.Status == "failed" {
			return fmt.Errorf("conversion failed")
		}
	}
	if dlURL == "" {
		return fmt.Errorf("timed out waiting for job")
	}

	// --- Step 3: Download ---
	req, _ = http.NewRequest("GET", dlURL, nil)
	req.Header.Set("Authorization", "Bearer "+apiKey)
	resp, err = client.Do(req)
	if err != nil {
		return fmt.Errorf("download: %w", err)
	}
	defer resp.Body.Close()

	out, err := os.Create(outputPath)
	if err != nil {
		return err
	}
	defer out.Close()
	_, err = io.Copy(out, resp.Body)
	fmt.Printf("Saved: %s
", outputPath)
	return err
}

func main() {
	if len(os.Args) != 3 {
		fmt.Fprintln(os.Stderr, "Usage: converter input.pdf output.epub")
		os.Exit(1)
	}
	if err := convertPDFToEPUB(os.Args[1], os.Args[2]); err != nil {
		fmt.Fprintln(os.Stderr, "Error:", err)
		os.Exit(1)
	}
}

Run with: TOOLKIT_API_KEY=tk_xxx go run main.go document.pdf document.epub

Concurrent batch conversion

Go's goroutines make concurrent batch processing straightforward. This example converts multiple PDFs in parallel, respecting a concurrency limit:

func batchConvert(paths []string, concurrency int) {
	sem := make(chan struct{}, concurrency)
	var wg sync.WaitGroup

	for _, p := range paths {
		wg.Add(1)
		sem <- struct{}{}
		go func(pdf string) {
			defer wg.Done()
			defer func() { <-sem }()
			out := pdf[:len(pdf)-4] + ".epub"
			if err := convertPDFToEPUB(pdf, out); err != nil {
				fmt.Fprintf(os.Stderr, "Failed %s: %v
", pdf, err)
			}
		}(p)
	}
	wg.Wait()
}

// Usage: convert up to 5 PDFs at once
// batchConvert(pdfFiles, 5)

Building a CLI tool

Package the converter as a standalone CLI using Go's flag package:

package main

import (
	"flag"
	"fmt"
	"os"
)

func main() {
	input  := flag.String("in",  "", "Input PDF path (required)")
	output := flag.String("out", "", "Output EPUB path (required)")
	flag.Parse()

	if *input == "" || *output == "" {
		flag.Usage()
		os.Exit(1)
	}
	if err := convertPDFToEPUB(*input, *output); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

Build a static binary: go build -o pdf2epub main.go

Cross-compile for Linux from macOS: GOOS=linux GOARCH=amd64 go build -o pdf2epub-linux main.go

FAQ

Does this require any external Go packages?

No. The complete example uses only Go standard library packages: net/http, mime/multipart, encoding/json, and io. No go get needed.

How do I handle context cancellation and timeouts?

Pass a context.Context to http.NewRequestWithContext for each request. Use context.WithTimeout to enforce an overall deadline across the upload + poll + download sequence.

What Go version is required?

Go 1.16 or later. All APIs used (including io.Copy optimizations and module support) are available since 1.16. The code also works on Go 1.21+.

Is there a Go module / package for toolkit.bot?

Not yet. The REST API is simple enough that the implementation above is all you need. Fork it or copy it directly into your project.

Get your free API key
Sign up at toolkit.bot/api — free tier, no credit card required.