PDF to EPUB Conversion in Go — toolkit.bot API Integration
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.
Sign up at toolkit.bot/api — free tier, no credit card required.