Skip to Content
SdksGo SDK

Go SDK

The voxburst-go package is the official Go client for the VoxBurst API. It provides context support, automatic retries, typed errors, and pagination helpers.

go get github.com/FortheanLabsProjects/voxburst-go

Requires Go 1.21 or later.

Official package. github.com/FortheanLabsProjects/voxburst-go is the official VoxBurst Go client, maintained by the VoxBurst team under the FortheanLabsProjects GitHub organization. The module path reflects the organization name, not a third-party fork.


What do you want to do?


Build your first Go integration

A guided path from go get to a published post.

package main import ( "context" "fmt" "log" "os" "time" "github.com/FortheanLabsProjects/voxburst-go/voxburst" ) func main() { // 1. Create the client client := voxburst.NewClient(os.Getenv("VOXBURST_API_KEY")) ctx := context.Background() // 2. Verify auth — list connected accounts accounts, err := client.Accounts.List(ctx, nil) if err != nil { log.Fatal(err) } fmt.Printf("Connected accounts: %d\n", len(accounts.Data)) for _, a := range accounts.Data { fmt.Printf(" %s %s (%s)\n", a.ID, a.Username, a.Platform) } // 3. Find the account to post to var twitterID string for _, a := range accounts.Data { if string(a.Platform) == "twitter" { twitterID = a.ID break } } // 4. Create a draft draft, err := client.Posts.CreateDraft(ctx, "Hello from VoxBurst Go SDK! 🚀", []string{twitterID}, ) if err != nil { log.Fatal(err) } fmt.Printf("Draft: %s (status: %s)\n", draft.ID, draft.Status) // Draft: post_abc123 (status: draft) // 5. Schedule it scheduledTime := time.Now().Add(24 * time.Hour) scheduled, err := client.Posts.Schedule(ctx, "Scheduled post from Go!", []string{twitterID}, scheduledTime, ) if err != nil { log.Fatal(err) } fmt.Printf("Scheduled for: %s\n", scheduled.ScheduledFor) // 6. Inspect result result, err := client.Posts.Get(ctx, draft.ID) if err != nil { log.Fatal(err) } fmt.Printf("Status: %s\n", result.Status) for _, p := range result.Platforms { fmt.Printf(" %s: %s%s\n", p.Platform, p.Status, p.PlatformPostURL) } // 7. Publish immediately published, err := client.Posts.Publish(ctx, draft.ID) if err != nil { if voxburst.IsRateLimited(err) { log.Printf("Rate limited — wait and retry") } else { log.Fatal(err) } } fmt.Printf("Published: %s\n", published.ID) }

Configuration

client := voxburst.NewClient(apiKey, voxburst.WithBaseURL("https://api.voxburst.io/v1"), voxburst.WithTimeout(30 * time.Second), voxburst.WithHTTPClient(&http.Client{Timeout: 60 * time.Second}), )
OptionDescription
WithBaseURL(url)Override API base URL (e.g. for staging)
WithTimeout(d)Request timeout — increase for large video uploads
WithHTTPClient(c)Provide a custom *http.Client

Complete Instagram example

Instagram requires ContentType and at least one media item. The full flow: upload → poll until READY → create post.

// Step 1: upload media mediaID, err := uploadMedia(ctx, client, "photo.jpg") if err != nil { log.Fatal(err) } // Step 2: find Instagram account accounts, _ := client.Accounts.List(ctx, nil) var igID string for _, a := range accounts.Data { if string(a.Platform) == "instagram" { igID = a.ID; break } } // Step 3: create the post post, err := client.Posts.Create(ctx, voxburst.CreatePostParams{ Content: "Behind the scenes 📸", AccountIds: []string{igID}, ContentType: "IMAGE", Media: []string{mediaID}, FirstComment: "#photography #launch", FirstCommentDelay: 30, }) if err != nil { log.Fatal(err) } fmt.Printf("Created: %s (status: %s)\n", post.ID, post.Status) // Step 4: check result result, _ := client.Posts.Get(ctx, post.ID) for _, p := range result.Platforms { fmt.Printf("%s: %s%s\n", p.Platform, p.Status, p.PlatformPostURL) }

Where uploadMedia implements the presigned-URL flow — see Media upload below.

post, err := client.Posts.Create(ctx, voxburst.CreatePostParams{ Content: "Swipe to see more →", AccountIds: []string{igID}, ContentType: "CAROUSEL", Media: []string{"media_slide1", "media_slide2", "media_slide3"}, })

Cross-platform example

Post the same content to multiple platforms with per-platform overrides.

// Collect account IDs for multiple platforms accounts, _ := client.Accounts.List(ctx, nil) var accountIDs []string for _, a := range accounts.Data { switch string(a.Platform) { case "instagram", "twitter", "linkedin": accountIDs = append(accountIDs, a.ID) } } post, err := client.Posts.Create(ctx, voxburst.CreatePostParams{ Content: "Announcing VoxBurst 2.0 — unified social scheduling.", AccountIds: accountIDs, ContentType: "IMAGE", Media: []string{"media_launch_image"}, PlatformOverrides: map[voxburst.Platform]any{ "TWITTER": map[string]any{ "content": "VoxBurst 2.0 just shipped 🚀 #ProductLaunch", }, "LINKEDIN": map[string]any{ "content": "Today we are shipping VoxBurst 2.0. Built for teams that publish across every platform at scale.", }, }, FirstComment: "#VoxBurst20 #Launch", FirstCommentDelay: 60, }) if err != nil { log.Fatal(err) } // After publishing, check each platform result, _ := client.Posts.Get(ctx, post.ID) for _, p := range result.Platforms { fmt.Printf("%s: %s%s\n", p.Platform, p.Status, p.PlatformPostURL) } // Retry any failures if result.Status == "partial" || result.Status == "failed" { retried, _ := client.Posts.Retry(ctx, post.ID) fmt.Printf("Retried %d platforms\n", len(retried.Platforms)) }

Schedule a post

scheduledTime := time.Date(2026, 6, 1, 15, 0, 0, 0, time.UTC) post, err := client.Posts.Schedule(ctx, "Launch day 🚀", []string{"acc_twitter_abc"}, scheduledTime, ) // post.Status == "scheduled" // post.ScheduledFor.Equal(scheduledTime)

Post status lifecycle

draft → scheduled → publishing → published ↓ ↓ failed partial ↓ ↓ Retry() Retry()
StatusUpdateRetryDeletePublish
draft
scheduled
publishing
published
failed
partial

Media upload

func uploadMedia(ctx context.Context, client *voxburst.Client, filename string) (string, error) { data, err := os.ReadFile(filename) if err != nil { return "", err } // Request presigned URL upload, err := client.Media.RequestUploadURL(ctx, voxburst.RequestUploadParams{ Filename: filepath.Base(filename), ContentType: "image/jpeg", SizeBytes: int64(len(data)), }) if err != nil { return "", err } // PUT bytes directly to S3 req, _ := http.NewRequestWithContext(ctx, http.MethodPut, upload.UploadURL, bytes.NewReader(data)) req.Header.Set("Content-Type", "image/jpeg") resp, err := http.DefaultClient.Do(req) if err != nil || resp.StatusCode != 200 { return "", fmt.Errorf("upload failed: status %d", resp.StatusCode) } // Poll until READY for { media, err := client.Media.Get(ctx, upload.MediaID) if err != nil { return "", err } switch media.Status { case voxburst.MediaStatusReady: return upload.MediaID, nil case voxburst.MediaStatusFailed: return "", fmt.Errorf("media processing failed") } time.Sleep(2 * time.Second) } }

Key response fields from Media.Get:

  • media.ID — the media ID to pass as Media: []string{mediaID} in posts
  • media.StatusMediaStatusPending, MediaStatusProcessing, MediaStatusReady, MediaStatusFailed
  • media.ContentType"image/jpeg" (VoxBurst normalizes PNG/WebP to JPEG)
  • media.PublicURL — CDN URL of the file after processing

Analytics (REST-only)

voxburst-go does not wrap analytics endpoints — use net/http directly. All 21 analytics routes follow the same pattern. See the Analytics API reference for the full endpoint list, query parameters, and response shapes.

import ( "encoding/json" "fmt" "net/http" "os" ) func fetchAnalytics(path string) (map[string]any, error) { req, _ := http.NewRequest("GET", "https://api.voxburst.io"+path, nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("VOXBURST_API_KEY")) resp, err := http.DefaultClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() var result map[string]any json.NewDecoder(resp.Body).Decode(&result) return result, nil } // Post-level metrics postMetrics, _ := fetchAnalytics("/v1/analytics/posts/post_abc123?startDate=2026-05-01&endDate=2026-05-31") // Account-level metrics acctMetrics, _ := fetchAnalytics("/v1/analytics/accounts/acc_123?startDate=2026-05-01&endDate=2026-05-31") // AI Insights (paid plans — requires system AI provider config) insights, _ := fetchAnalytics("/v1/analytics/insights") // Hashtag performance hashtags, _ := fetchAnalytics("/v1/analytics/hashtag-performance?startDate=2026-05-01&endDate=2026-05-31&limit=10") _ = fmt.Sprintf("%v %v %v %v", postMetrics, acctMetrics, insights, hashtags)

Error handling

import "github.com/FortheanLabsProjects/voxburst-go/voxburst" _, err := client.Posts.Get(ctx, "post_notfound") if err != nil { switch { case voxburst.IsNotFound(err): log.Println("post not found") case voxburst.IsUnauthorized(err): log.Println("invalid API key — check VOXBURST_API_KEY") case voxburst.IsRateLimited(err): log.Println("rate limited — back off and retry") time.Sleep(60 * time.Second) case voxburst.IsServerError(err): log.Println("VoxBurst server error — transient, retry") default: log.Fatalf("unexpected error: %v", err) } }
HelperHTTP statusWhen to use
voxburst.IsNotFound(err)404Resource does not exist
voxburst.IsUnauthorized(err)401Invalid or expired API key
voxburst.IsRateLimited(err)429Too many requests — back off
voxburst.IsServerError(err)5xxTransient server error — retry

Retry vs recreate:

SituationAction
429 rate limitWait and retry the same call
5xx server errorRetry up to 3 times with backoff
401 unauthorizedFix the API key — do not retry
Post status failedCall client.Posts.Retry(ctx, id)
Post status partialCall client.Posts.Retry(ctx, id)
Media processing failedRe-upload with a new file

Pagination

// List() — one page at a time resp, err := client.Posts.List(ctx, &voxburst.ListPostsParams{ Status: voxburst.PostStatusScheduled, Limit: 20, }) // resp.Data — current page // resp.Pagination.HasMore — more pages available // resp.Pagination.NextCursor — pass as Cursor on the next call if resp.Pagination.HasMore { next, _ := client.Posts.List(ctx, &voxburst.ListPostsParams{ Cursor: resp.Pagination.NextCursor, }) } // ListAll() — auto-paginates; use for processing everything iter := client.Posts.ListAll(ctx, &voxburst.ListPostsParams{Limit: 50}) for iter.Next() { p := iter.Post() fmt.Printf("%s: %s\n", p.ID, p.Status) } if err := iter.Err(); err != nil { log.Fatal(err) }

When to use what:

  • List() — when you need one page or want to check HasMore before continuing
  • ListAll() — when you need every result (export, sync, analytics pipeline)

Next steps

Last updated on