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
- Messaging API reference
- Subscribers API
- Other languages: Node · Python · PHP