Skip to main content

Design Philosophy

The OnlyFans SDK is built around developer experience, type safety, and reliability. Every design decision prioritizes making the OnlyFans API accessible, predictable, and safe to use.

Type Safety First

Full TypeScript support with compile-time validation, IntelliSense, and structured error handling

Modular Architecture

Organized by functionality - each module handles a specific aspect of the OnlyFans platform

Core Architecture

SDK Instance

The SDK is initialized as a single instance that provides access to all modules:
import OFSDK from "@ofauth/onlyfans-sdk"

// Single SDK instance with global configuration
const sdk = new OFSDK({
  mode: "access",           // ✅ Validated mode options
  ofauthApiKey: "your-key", // ✅ Required for managed access
  debugLog: false           // ✅ Optional debugging
})

// Access modules through the instance
sdk.messages    // 💬 Messaging operations
sdk.posts       // 📝 Content management
sdk.user        // 👤 User operations
sdk.vault       // 🗃️ Media management
// ... all other modules

Module Structure

Each module is self-contained and focused on a specific domain:
// Each module has consistent method signatures
interface ModulePattern {
  // All methods follow the same pattern
  async operation({
    authentication: AuthOptions,    // ✅ Required auth context
    ...specificParams              // ✅ Operation-specific parameters
  }): Promise<ApiResponse<T>>      // ✅ Typed response
}

// Example: Messages module
const result = await sdk.messages.getChatsList({
  authentication: { connectionId: "conn_123" },
  params: { limit: 20, offset: 0 }
})

Type Safety System

Automatic Parameter Validation

The SDK validates all parameters at compile time and runtime:
// ✅ TypeScript catches invalid parameters before runtime
const result = await sdk.statistics.getStats({
  authentication: { connectionId: "conn_123" },
  statType: "posts",              // ✅ Validates against allowed enum values
  params: {
    by: "purchases",              // ✅ Context-aware validation (valid for "posts")
    startDate: new Date("2024-01-01"), // ✅ Date object validation
    limit: 20                     // ✅ Number type enforcement
  }
})

// ❌ TypeScript prevents invalid combinations at compile time
const invalid = await sdk.statistics.getStats({
  statType: "posts",
  params: {
    by: "revenue"                 // ❌ Not valid for "posts" statType
  }
})

Response Type Safety

All API responses are fully typed with IntelliSense support:
const result = await sdk.messages.getChatsList({ 
  authentication: { connectionId } 
})

if (result.data) {
  // ✅ TypeScript knows the exact structure
  result.data.list.forEach(chat => {
    console.log(chat.user.username)    // ✅ IntelliSense available
    console.log(chat.lastMessage.text) // ✅ Nested properties typed
    console.log(chat.unreadCount)      // ✅ Number types enforced
  })
}

Authentication Architecture

Flexible Authentication Options

The SDK supports multiple authentication modes with automatic validation:
// Connection-based authentication (recommended)
const connectionAuth = {
  authentication: { connectionId: "conn_123" }
}

// Session-based authentication (for direct access)
const sessionAuth = {
  authentication: { 
    session: {
      "user-id": "123456789",
      "user-agent": "...",
      "cookie": "..."
    }
  }
}

// The SDK automatically validates which auth method is required
// ✅ Some operations require sessions, others accept connections
await sdk.subscriptions.subscribeToUser({
  authentication: { session: userData }, // ✅ Session required for user actions
  userId: 123456789
})

Mode-Based Operation

The SDK adapts its behavior based on the configured mode:
// Direct OnlyFans API access
const sdk = new OFSDK({
  mode: "direct",
  ofauthApiKey: "your-api-key" // For dynamic rules
})

// ✅ Full control over requests
// ✅ Custom session management
// ⚠️ Manual rate limiting required
// ⚠️ Complex request signing
// Custom proxy/middleware
const sdk = new OFSDK({
  mode: "custom",
  customOptions: {
    baseUrl: "https://your-proxy.com",
    signRequests: true,
    beforeRequest: (url, request) => {
      // Custom request modification
      return request
    }
  }
})

Error Handling Architecture

Structured Error Types

All errors follow a consistent structure with type safety:
interface ErrorResponse<T = any> {
  error: {
    statusCode: number          // ✅ HTTP status
    type: ErrorType            // ✅ Categorized error types
    message: string            // ✅ Human-readable message
    retry: boolean             // ✅ Whether operation can be retried
    details?: T                // ✅ Context-specific error data
  }
}

// Usage with type safety
const result = await sdk.user.getUserInfo(params)

if (result.error) {
  switch (result.error.type) {
    case "unauthorized":
      // Handle session expiry
      break
    case "forbidden":
      // Handle permission issues
      break
    case "bad_request":
      // result.error.details is typed based on the operation
      console.log("Validation errors:", result.error.details)
      break
  }
}

Module Organization

The SDK is organized into 10 focused modules:

Communication

Messages - Chat, mass messaging, queued messages

Media

Vault - File management, media organization, downloads

Content

Posts - Content creation, management, scheduling

Users

User - Profile data, user interactions, search

Authentication

Link - Connection management, authentication flows

Account

Account - Webhooks, account settings

Profile

Self - Own profile management, settings, notifications

Subscriptions

Subscriptions - Subscriber management, lists

Analytics

Statistics - Performance metrics, earnings data

Upload

Upload - File uploads, progress tracking

Request Flow Architecture

Unified Request Pipeline

All SDK operations follow the same request flow:

Automatic Request Enhancement

The SDK automatically enhances every request:
// What you write
const result = await sdk.posts.createPost({
  authentication: { connectionId: "conn_123" },
  params: {
    text: "Hello world!",
    price: 5.00
  }
})

// What the SDK does internally:
// 1. ✅ Validates all parameters (TypeScript + runtime)
// 2. ✅ Adds required headers (user-agent, content-type, etc.)
// 3. ✅ Signs request with dynamic rules (if needed)
// 4. ✅ Handles authentication context
// 5. ✅ Manages rate limiting
// 6. ✅ Processes response and provides typed data
// 7. ✅ Converts errors to structured format

Development Experience

IntelliSense and Autocomplete

The SDK provides comprehensive IDE support:
// ✅ Autocomplete shows all available modules
sdk. // → messages, posts, user, vault, etc.

// ✅ Method signatures with parameter descriptions
sdk.messages.createQueuedMessage({
  authentication: { connectionId: "..." },
  params: {
    // ✅ IntelliSense shows all valid parameters
    text: "...",        // String - Message content
    price: 5.00,        // Number - Message price
    lockedText: "...",  // String - Preview text
    // ... more parameters with descriptions
  }
})

Runtime Validation

Beyond TypeScript, the SDK provides runtime safety:
// Even with TypeScript disabled, the SDK validates:
// ✅ Required parameters are present
// ✅ Parameter types are correct
// ✅ Enum values are valid
// ✅ Authentication context is appropriate
// ✅ Date formats are valid
// ✅ Array elements are correctly typed

const result = await sdk.statistics.getStats({
  // Runtime validation ensures this is safe
  statType: userInput,  // Validated against allowed values
  params: {
    startDate: new Date(userDateInput)  // Date validation
  }
})

Performance Architecture

Optimized for Scale

The SDK is designed for high-performance applications:
// ✅ Efficient batch operations
const [messages, posts, stats] = await Promise.all([
  sdk.messages.getChatsList({ authentication: { connectionId } }),
  sdk.posts.getMyPosts({ authentication: { connectionId } }),
  sdk.statistics.getStats({ 
    authentication: { connectionId },
    statType: "posts" 
  })
])

// ✅ Built-in pagination support
async function getAllChats(connectionId: string) {
  let allChats = []
  let offset = 0
  const limit = 50  // SDK validates pagination parameters
  
  while (true) {
    const result = await sdk.messages.getChatsList({
      authentication: { connectionId },
      params: { limit, offset }
    })
    
    if (!result.data?.list.length) break
    
    allChats.push(...result.data.list)
    offset += limit
  }
  
  return allChats
}

Best Practices

Module Focus

Use the Right Module
  • Each module has a specific purpose
  • Don’t mix concerns between modules
  • Leverage module-specific optimizations
  • Follow TypeScript guidance for parameter validation

Error Recovery

Handle Errors Gracefully
  • Check result.error before using result.data
  • Use error types for appropriate responses
  • Implement retry logic for retriable errors
  • Log errors with sufficient context
Next Steps: Now that you understand the architecture, explore individual modules in the API Reference section to see how these principles apply to specific operations.