ofapisv1
quickstart

OnlyFans API with Go

Calling the OnlyFans API from Go (Golang) needs no SDK and no code generation: ofapis is a plain REST API with Bearer auth, so Go's standard net/http is all you need. This quickstart takes you from token to a sent message. The base URL for every request is https://api.ofapis.com/api/public/v1.

1. Get a token

Create an ofapis_sk_... token in the dashboard (start free) and keep it server-side. Read it from the environment (os.Getenv) rather than hard-coding it. See authentication.

2. Set up the module

net/http and encoding/json are in the standard library, so no external dependency is required:

go mod init example.com/ofapis-demo

3. Verify the token — GET /me

A small helper keeps the Authorization header on every request. GET /me returns the connected creator and confirms the token works:

package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
)

const base = "https://api.ofapis.com/api/public/v1"

var token = os.Getenv("OFAPIS_KEY")

func do(method, path string, body io.Reader) (*http.Response, error) {
	req, err := http.NewRequest(method, base+path, body)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Authorization", "Bearer "+token)
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	return http.DefaultClient.Do(req)
}

func main() {
	res, err := do("GET", "/me", nil)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
	if res.StatusCode != http.StatusOK {
		b, _ := io.ReadAll(res.Body)
		panic(fmt.Sprintf("/me failed: %d %s", res.StatusCode, b))
	}
	var me struct{ Username string `json:"username"` }
	json.NewDecoder(res.Body).Decode(&me)
	fmt.Println("connected as", me.Username)
}

4. List chats

res, _ := do("GET", "/chats?limit=50", nil)
defer res.Body.Close()

var out struct {
	Data []struct{ ID string `json:"id"` } `json:"data"`
}
json.NewDecoder(res.Body).Decode(&out)
for _, c := range out.Data {
	fmt.Println(c.ID)
}

5. Send a message

payload := strings.NewReader(`{"text":"Thanks for subscribing!"}`)
res, err := do("POST", "/chats/123/messages", payload)
if err != nil {
	log.Fatal(err) // network error — nothing was charged
}
defer res.Body.Close()

switch res.StatusCode {
case 200:
	fmt.Println("delivered — 1 credit spent")
case 401:
	log.Fatal("bad or revoked token")
case 429:
	log.Println("rate limited — back off and retry")
default:
	b, _ := io.ReadAll(res.Body)
	log.Printf("send failed: %d %s", res.StatusCode, b)
}

A 200 means delivered and one credit spent. Failed calls — expired session, 429, upstream 5xx — are never charged, so a retry after failure is free.

Agencies: scope by account

Managing several creators? Address each by id under scoped routes, e.g. do("GET", "/accounts/42/chats", nil). See agency automation.

FAQ

Is there an official Go SDK for the OnlyFans API?

No. ofapis is a standard REST API, so net/http plus encoding/json is enough. If you prefer generated types, run an OpenAPI generator against the spec at /docs.

How do I handle rate limits in Go?

Check for HTTP 429 in the response and retry after a short backoff. Plan ceilings range from 60 rpm on Free to 500 rpm on Pro — see pricing.

Does a network error or 5xx cost a credit?

No. Billing is metered per successful call: one 2xx response equals one credit. Transport errors, 429s, and upstream 5xx responses are not charged.

Should I reuse the http.Client?

Yes. http.DefaultClient reuses connections; for production, create one http.Client with a Timeout and share it across goroutines rather than allocating per request.

Next steps

Related: Rust · curl

Related guides

All guides
Start free — 250 credits, no card
Generate a token and make your first call in minutes.
Get started