API Authentication Guide

Simplified authentication for OptionsPlay® RESTful APIs

Overview

OptionsPlay is introducing a streamlined authentication method for our RESTful APIs. The new approach eliminates the need for OAuth token management, reducing your integration complexity from multiple steps to a single API key.

📋 Both Methods Supported

The legacy OAuth approach continues to work. You can migrate at your convenience—there's no deadline to switch. However, we recommend the new approach for simpler integration and reduced maintenance.

What's Changing

sequenceDiagram participant C as Your Application participant A as OptionsPlay APIs rect rgb(240, 240, 240) Note over C,A: Legacy Approach (2 steps) C->>A: POST /token (clientId + clientSecret) A-->>C: Bearer Token (expires in ~60 min) C->>A: GET /api/... + Authorization: Bearer {token} A-->>C: Response end rect rgb(230, 244, 234) Note over C,A: New Approach (1 step) C->>A: GET /api/... + Op-Subscription-Key: {apiKey} A-->>C: Response end

The new approach handles OAuth behind the scenes. You simply include your subscription key with each request—no token acquisition, no token refresh, no expiration handling.

Quick Comparison

Legacy Approach
  • Two credentials (clientId + clientSecret)
  • Requires token endpoint call
  • Tokens expire (~60 minutes)
  • You manage token refresh logic
  • Base URL: app.optionsplay.com
  • Header: Authorization: Bearer ...
✓ New Approach
  • Single API key
  • No token endpoint needed
  • Key doesn't expire
  • No refresh logic required
  • Base URL: apis.optionsplay.com
  • Header: Op-Subscription-Key: ...
Aspect Legacy New
Credentials clientId + clientSecret Single API key
Base URL https://app.optionsplay.com https://apis.optionsplay.com
Auth Header Authorization: Bearer {token} Op-Subscription-Key: {apiKey}
Token Management Required (acquire + refresh) Not needed
Usage Analytics Limited Full portal access

New Approach: Subscription Key

With the new approach, include your subscription key in the Op-Subscription-Key header with every request. That's it.

Request Format

curl -X GET "https://apis.optionsplay.com/api/v1/platform/why/AAPL" \
  -H "Op-Subscription-Key: YOUR_API_KEY"

Authentication Flow

sequenceDiagram participant App as Your Application participant APIM as OptionsPlay API Gateway participant API as OptionsPlay Backend App->>APIM: Request + Op-Subscription-Key APIM->>APIM: Validate subscription key APIM->>APIM: Acquire OAuth token (cached) APIM->>API: Request + Bearer token API-->>APIM: Response APIM-->>App: Response Note over APIM: Token caching & refresh
handled automatically
✓ No Code Changes for Token Refresh

The API gateway manages OAuth tokens internally. Your code simply sends the subscription key—no need to handle token expiration, refresh logic, or retry mechanisms.

Legacy Approach: OAuth Bearer Token

The legacy approach remains fully supported. If you have an existing integration, it will continue to work without changes.

Step 1: Acquire Token

curl -X POST "https://app.optionsplay.com/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"

Step 2: Use Token

curl -X GET "https://app.optionsplay.com/api/v1/platform/why/AAPL" \
  -H "Authorization: Bearer {access_token}"

Token Lifecycle

sequenceDiagram participant App as Your Application participant Auth as Token Endpoint participant API as OptionsPlay API App->>Auth: POST /token (credentials) Auth-->>App: access_token (expires_in: 3600) loop Every API Call App->>App: Check token expiration alt Token Valid App->>API: Request + Bearer token API-->>App: Response else Token Expired App->>Auth: POST /token (credentials) Auth-->>App: New access_token App->>API: Request + Bearer token API-->>App: Response end end

Migration Steps

Migrating to the new approach takes just a few minutes:

Get Your Subscription Key

Contact your OptionsPlay account representative or email support@optionsplay.com to receive your new API subscription key.

Update Base URL

Change your API base URL from app.optionsplay.com to apis.optionsplay.com. The API paths remain the same.

Replace Auth Header

Replace Authorization: Bearer {token} with Op-Subscription-Key: {apiKey} in your requests.

Remove Token Logic

Delete your token acquisition and refresh code. It's no longer needed—enjoy the simpler codebase!

Code Examples

Before (Legacy)

# Step 1: Get token
TOKEN=$(curl -s -X POST "https://app.optionsplay.com/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" | jq -r '.access_token')

# Step 2: Call API
curl -X GET "https://app.optionsplay.com/api/v1/platform/why/AAPL" \
  -H "Authorization: Bearer $TOKEN"
import requests
from datetime import datetime, timedelta

class OptionsPlayClient:
    def __init__(self, client_id, client_secret):
        self.client_id = client_id
        self.client_secret = client_secret
        self.base_url = "https://app.optionsplay.com"
        self.token = None
        self.token_expiry = None
    
    def _get_token(self):
        """Acquire or refresh OAuth token"""
        if self.token and self.token_expiry > datetime.now():
            return self.token
        
        response = requests.post(
            f"{self.base_url}/token",
            data={
                "grant_type": "client_credentials",
                "client_id": self.client_id,
                "client_secret": self.client_secret
            }
        )
        response.raise_for_status()
        data = response.json()
        
        self.token = data["access_token"]
        self.token_expiry = datetime.now() + timedelta(seconds=data["expires_in"] - 60)
        return self.token
    
    def get_why(self, symbol):
        """Get trade ideas for a symbol"""
        token = self._get_token()
        response = requests.get(
            f"{self.base_url}/api/v1/platform/why/{symbol}",
            headers={"Authorization": f"Bearer {token}"}
        )
        response.raise_for_status()
        return response.json()

# Usage
client = OptionsPlayClient("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET")
data = client.get_why("AAPL")
class OptionsPlayClient {
  constructor(clientId, clientSecret) {
    this.clientId = clientId;
    this.clientSecret = clientSecret;
    this.baseUrl = 'https://app.optionsplay.com';
    this.token = null;
    this.tokenExpiry = null;
  }

  async getToken() {
    // Return cached token if still valid
    if (this.token && this.tokenExpiry > Date.now()) {
      return this.token;
    }

    const response = await fetch(`${this.baseUrl}/token`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
      body: new URLSearchParams({
        grant_type: 'client_credentials',
        client_id: this.clientId,
        client_secret: this.clientSecret
      })
    });

    if (!response.ok) throw new Error('Token acquisition failed');
    
    const data = await response.json();
    this.token = data.access_token;
    this.tokenExpiry = Date.now() + (data.expires_in - 60) * 1000;
    return this.token;
  }

  async getWhy(symbol) {
    const token = await this.getToken();
    const response = await fetch(
      `${this.baseUrl}/api/v1/platform/why/${symbol}`,
      { headers: { 'Authorization': `Bearer ${token}` } }
    );
    
    if (!response.ok) throw new Error('API request failed');
    return response.json();
  }
}

// Usage
const client = new OptionsPlayClient('YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET');
const data = await client.getWhy('AAPL');
public class OptionsPlayClient
{
    private readonly string _clientId;
    private readonly string _clientSecret;
    private readonly string _baseUrl = "https://app.optionsplay.com";
    private readonly HttpClient _httpClient;
    private string _token;
    private DateTime _tokenExpiry;

    public OptionsPlayClient(string clientId, string clientSecret)
    {
        _clientId = clientId;
        _clientSecret = clientSecret;
        _httpClient = new HttpClient();
    }

    private async Task<string> GetTokenAsync()
    {
        if (_token != null && _tokenExpiry > DateTime.UtcNow)
            return _token;

        var content = new FormUrlEncodedContent(new[]
        {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            new KeyValuePair<string, string>("client_id", _clientId),
            new KeyValuePair<string, string>("client_secret", _clientSecret)
        });

        var response = await _httpClient.PostAsync($"{_baseUrl}/token", content);
        response.EnsureSuccessStatusCode();

        var json = await response.Content.ReadAsStringAsync();
        var tokenResponse = JsonSerializer.Deserialize<TokenResponse>(json);

        _token = tokenResponse.AccessToken;
        _tokenExpiry = DateTime.UtcNow.AddSeconds(tokenResponse.ExpiresIn - 60);
        return _token;
    }

    public async Task<string> GetWhyAsync(string symbol)
    {
        var token = await GetTokenAsync();
        _httpClient.DefaultRequestHeaders.Authorization = 
            new AuthenticationHeaderValue("Bearer", token);

        var response = await _httpClient.GetAsync(
            $"{_baseUrl}/api/v1/platform/why/{symbol}");
        response.EnsureSuccessStatusCode();

        return await response.Content.ReadAsStringAsync();
    }
}

// Usage
var client = new OptionsPlayClient("YOUR_CLIENT_ID", "YOUR_CLIENT_SECRET");
var data = await client.GetWhyAsync("AAPL");

After (New Approach)

# Single step - no token needed!
curl -X GET "https://apis.optionsplay.com/api/v1/platform/why/AAPL" \
  -H "Op-Subscription-Key: YOUR_API_KEY"
import requests

class OptionsPlayClient:
    def __init__(self, api_key):
        self.api_key = api_key
        self.base_url = "https://apis.optionsplay.com"
    
    def get_why(self, symbol):
        """Get trade ideas for a symbol"""
        response = requests.get(
            f"{self.base_url}/api/v1/platform/why/{symbol}",
            headers={"Op-Subscription-Key": self.api_key}
        )
        response.raise_for_status()
        return response.json()

# Usage - that's it!
client = OptionsPlayClient("YOUR_API_KEY")
data = client.get_why("AAPL")
class OptionsPlayClient {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = 'https://apis.optionsplay.com';
  }

  async getWhy(symbol) {
    const response = await fetch(
      `${this.baseUrl}/api/v1/platform/why/${symbol}`,
      { headers: { 'Op-Subscription-Key': this.apiKey } }
    );
    
    if (!response.ok) throw new Error('API request failed');
    return response.json();
  }
}

// Usage - that's it!
const client = new OptionsPlayClient('YOUR_API_KEY');
const data = await client.getWhy('AAPL');
public class OptionsPlayClient
{
    private readonly string _apiKey;
    private readonly string _baseUrl = "https://apis.optionsplay.com";
    private readonly HttpClient _httpClient;

    public OptionsPlayClient(string apiKey)
    {
        _apiKey = apiKey;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Op-Subscription-Key", _apiKey);
    }

    public async Task<string> GetWhyAsync(string symbol)
    {
        var response = await _httpClient.GetAsync(
            $"{_baseUrl}/api/v1/platform/why/{symbol}");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}

// Usage - that's it!
var client = new OptionsPlayClient("YOUR_API_KEY");
var data = await client.GetWhyAsync("AAPL");
📉 Code Reduction

Notice how the new approach eliminates ~50-70% of your authentication code. No token class, no expiry tracking, no refresh logic—just a simple header.

Developer Portal

With the new subscription key approach, you gain access to the OptionsPlay Developer Portal for self-service management and analytics.

📊

OptionsPlay Developer Portal

View usage reports, manage your subscription, and explore API documentation.

developer.optionsplay.com →

Portal Features

Feature Description
Usage Analytics View request counts, response times, and error rates over time
API Explorer Test API endpoints directly from your browser
Key Management Regenerate your subscription key if needed
Documentation Access detailed API reference and guides

Frequently Asked Questions

Do I have to migrate right away?
No. Both authentication methods are fully supported. You can migrate at your convenience or continue using the legacy OAuth approach indefinitely.
What happens to my existing clientId and clientSecret?
They continue to work. If you choose not to migrate, your existing credentials and integration will function exactly as before.
Are the API endpoints the same?
Yes, the API paths are identical. Only the base URL changes (from app.optionsplay.com to apis.optionsplay.com) and the authentication header.
Does my subscription key expire?
No. Unlike OAuth tokens, your subscription key does not expire. You can regenerate it through the Developer Portal if needed for security purposes.
Can I use both methods during migration?
Yes. You can run both approaches in parallel—for example, migrating one service at a time while others continue using the legacy method.
Is the new approach as secure?
Yes. The subscription key approach uses the same OAuth authentication behind the scenes—the API gateway handles token management securely on your behalf. All traffic is encrypted via HTTPS.
How do I get my subscription key?
Contact your OptionsPlay account representative or email support@optionsplay.com. We'll provision your key and provide Developer Portal access.

Support

Need help with your integration? We're here to assist.

Channel Contact
Email Support support@optionsplay.com
Developer Portal developer.optionsplay.com
API Reference Swagger Documentation