Class: SecApi::Client

Inherits:
Object
  • Object
show all
Includes:
CallbackHelper
Defined in:
lib/sec_api/client.rb

Overview

Note:

Client instances are thread-safe. All response objects are immutable (using Dry::Struct) and can be safely shared between threads.

Main entry point for interacting with the sec-api.io API.

The Client manages HTTP connections, configuration, and provides access to specialized proxy objects for different API endpoints. It uses a Client-Proxy architecture where the Client handles connection lifecycle and configuration, while proxy objects handle domain-specific operations.

Examples:

Create a client with API key

client = SecApi::Client.new(api_key: "your_api_key")

Create a client with full configuration

config = SecApi::Config.new(
  api_key: "your_api_key",
  base_url: "https://api.sec-api.io",
  request_timeout: 60,
  retry_max_attempts: 5,
  default_logging: true,
  logger: Rails.logger
)
client = SecApi::Client.new(config)

Access different API endpoints via proxies

client.query      # => SecApi::Query (filing searches)
client.mapping    # => SecApi::Mapping (entity resolution)
client.extractor  # => SecApi::Extractor (document extraction)
client.xbrl       # => SecApi::Xbrl (XBRL data extraction)
client.stream     # => SecApi::Stream (real-time WebSocket)

Monitor rate limit status

summary = client.rate_limit_summary
puts "Remaining: #{summary[:remaining]}/#{summary[:limit]}"
puts "Queued: #{summary[:queued_count]}"

See Also:

Instance Method Summary collapse

Methods included from CallbackHelper

#log_callback_error

Constructor Details

#initialize(config = Config.new) ⇒ SecApi::Client

Creates a new SEC API client.

Examples:

Create with API key only

client = SecApi::Client.new(api_key: "your_api_key")

Create with environment variable

# Set SECAPI_API_KEY environment variable
client = SecApi::Client.new

Create with full configuration

client = SecApi::Client.new(
  api_key: "your_api_key",
  request_timeout: 60,
  retry_max_attempts: 5,
  default_logging: true,
  logger: Logger.new($stdout)
)

Parameters:

  • config (SecApi::Config, Hash) (defaults to: Config.new)

    Configuration object or hash with config options. If a Hash is provided, it’s passed to Config.new. If omitted, uses environment variables and defaults.

Options Hash (config):

  • :api_key (String)

    SEC API key (required, or set SECAPI_API_KEY env var)

  • :base_url (String)

    Base API URL (default: “api.sec-api.io”)

  • :request_timeout (Integer)

    Request timeout in seconds (default: 30)

  • :retry_max_attempts (Integer)

    Maximum retry attempts (default: 3)

  • :default_logging (Boolean)

    Enable default structured logging (default: false)

  • :logger (Logger)

    Logger instance for logging events

Raises:



84
85
86
87
88
89
90
# File 'lib/sec_api/client.rb', line 84

def initialize(config = Config.new)
  @_config = config
  @_config.validate!
  setup_default_logging if @_config.default_logging && @_config.logger
  setup_default_metrics if @_config.metrics_backend
  @_rate_limit_tracker = RateLimitTracker.new
end

Instance Method Details

#configSecApi::Config

Returns the configuration object for this client.

Examples:

Access configuration settings

client.config.api_key       # => "your_api_key"
client.config.base_url      # => "https://api.sec-api.io"
client.config.request_timeout  # => 30

Returns:



101
102
103
# File 'lib/sec_api/client.rb', line 101

def config
  @_config
end

#connectionFaraday::Connection

Note:

In most cases, use the proxy objects (query, mapping, etc.) instead of making direct connection requests. The proxies provide type-safe response handling and a cleaner API.

Returns the Faraday connection used for HTTP requests.

The connection is lazily initialized on first access and includes the full middleware stack: JSON encoding/decoding, instrumentation, retry logic, rate limiting, and error handling.

Examples:

Make a direct request (advanced usage)

response = client.connection.get("/mapping/ticker/AAPL")
puts response.body

Returns:

  • (Faraday::Connection)

    Configured Faraday connection



121
122
123
# File 'lib/sec_api/client.rb', line 121

def connection
  @_connection ||= build_connection
end

#extractorSecApi::Extractor

Returns the Extractor proxy for document extraction functionality.

Provides access to the sec-api.io document extraction API for extracting text and specific sections from SEC filings.

Examples:

Extract full filing text

filing = client.query.ticker("AAPL").form_type("10-K").search.first
extracted = client.extractor.extract(filing.url)
puts extracted.text

Extract specific sections

extracted = client.extractor.extract(filing.url, sections: [:risk_factors, :mda])
puts extracted.risk_factors
puts extracted.mda

Returns:

See Also:



159
160
161
# File 'lib/sec_api/client.rb', line 159

def extractor
  @_extractor ||= Extractor.new(self)
end

#mappingSecApi::Mapping

Returns the Mapping proxy for entity resolution functionality.

Provides access to the sec-api.io mapping API for resolving between different entity identifiers: ticker symbols, CIK numbers, CUSIP, and company names.

Examples:

Resolve ticker to company entity

entity = client.mapping.ticker("AAPL")
puts "CIK: #{entity.cik}, Name: #{entity.name}"

Resolve CIK to ticker

entity = client.mapping.cik("320193")
puts "Ticker: #{entity.ticker}"

Resolve CUSIP

entity = client.mapping.cusip("037833100")

Returns:

See Also:



184
185
186
# File 'lib/sec_api/client.rb', line 184

def mapping
  @_mapping ||= Mapping.new(self)
end

#querySecApi::Query

Returns a fresh Query builder instance for constructing SEC filing searches.

Unlike other proxy methods, this returns a NEW instance on each call to ensure query chains start with fresh state.

Examples:

Each call starts fresh

client.query.ticker("AAPL").search  # Query: "ticker:AAPL"
client.query.ticker("TSLA").search  # Query: "ticker:TSLA" (not "ticker:AAPL AND ticker:TSLA")

Returns:



136
137
138
# File 'lib/sec_api/client.rb', line 136

def query
  Query.new(self)
end

#queued_requestsInteger

Returns the number of requests currently queued waiting for rate limit reset.

When the rate limit is exhausted (remaining = 0), incoming requests are queued until the rate limit window resets. This method returns the current count of waiting requests, useful for monitoring and debugging.

Examples:

Monitor queue depth

client = SecApi::Client.new
# During heavy load when rate limited:
puts "#{client.queued_requests} requests waiting"

Logging queue status

config = SecApi::Config.new(
  api_key: "...",
  on_queue: ->(info) {
    puts "Request queued (#{info[:queue_size]} total waiting)"
  }
)

Returns:

  • (Integer)

    Number of requests currently waiting in queue



275
276
277
# File 'lib/sec_api/client.rb', line 275

def queued_requests
  @_rate_limit_tracker.queued_count
end

#rate_limit_stateRateLimitState?

Returns the current rate limit state from the most recent API response.

The state is automatically updated after each API request based on X-RateLimit-* headers returned by sec-api.io.

Examples:

Check rate limit status after requests

client = SecApi::Client.new
client.query.ticker("AAPL").search

state = client.rate_limit_state
puts "Remaining: #{state.remaining}/#{state.limit}"
puts "Resets at: #{state.reset_at}"

Proactive throttling based on remaining quota

state = client.rate_limit_state
if state&.percentage_remaining && state.percentage_remaining < 10
  # Less than 10% remaining, consider slowing down
  sleep(1)
end

Handle exhausted rate limit

if client.rate_limit_state&.exhausted?
  wait_time = client.rate_limit_state.reset_at - Time.now
  sleep(wait_time) if wait_time.positive?
end

Returns:

  • (RateLimitState, nil)

    The current rate limit state, or nil if no rate limit headers have been received yet



250
251
252
# File 'lib/sec_api/client.rb', line 250

def rate_limit_state
  @_rate_limit_tracker.current_state
end

#rate_limit_summaryHash

Returns a summary of the current rate limit state for debugging and monitoring.

Provides a comprehensive view of the rate limit status in a single method call, useful for debugging, logging, and monitoring dashboards.

Examples:

Quick debugging

client = SecApi::Client.new
client.query.ticker("AAPL").search

pp client.rate_limit_summary
# => {:remaining=>95, :limit=>100, :percentage=>95.0,
#     :reset_at=>2024-01-15 10:30:00 +0000, :queued_count=>0, :exhausted=>false}

Health check endpoint

get '/health/rate_limit' do
  json client.rate_limit_summary
end

Returns:

  • (Hash)

    Rate limit summary with the following keys:

    • :remaining [Integer, nil] - Requests remaining in current window

    • :limit [Integer, nil] - Total requests allowed per window

    • :percentage [Float, nil] - Percentage of quota remaining (0.0-100.0)

    • :reset_at [Time, nil] - When the rate limit window resets

    • :queued_count [Integer] - Number of requests currently queued

    • :exhausted [Boolean] - True if rate limit is exhausted (remaining = 0)



305
306
307
308
309
310
311
312
313
314
315
# File 'lib/sec_api/client.rb', line 305

def rate_limit_summary
  state = rate_limit_state
  {
    remaining: state&.remaining,
    limit: state&.limit,
    percentage: state&.percentage_remaining,
    reset_at: state&.reset_at,
    queued_count: queued_requests,
    exhausted: state&.exhausted? || false
  }
end

#streamSecApi::Stream

Note:

The subscribe method blocks while receiving events. For non-blocking operation, run in a separate thread.

Returns the Stream proxy for real-time filing notifications via WebSocket.

Examples:

Subscribe to real-time filings

client = SecApi::Client.new
client.stream.subscribe do |filing|
  puts "New filing: #{filing.ticker} - #{filing.form_type}"
end

Close the streaming connection

client.stream.close

Returns:

  • (SecApi::Stream)

    Stream proxy instance for WebSocket subscriptions



217
218
219
# File 'lib/sec_api/client.rb', line 217

def stream
  @_stream ||= Stream.new(self)
end

#xbrlSecApi::Xbrl

Returns the XBRL extraction proxy for accessing XBRL-to-JSON conversion functionality.

Examples:

Extract XBRL data from a filing

client = SecApi::Client.new(api_key: "your_api_key")
xbrl_data = client.xbrl.to_json(filing)
xbrl_data.financials[:revenue]  # => 394328000000.0

Returns:

  • (SecApi::Xbrl)

    XBRL proxy instance with access to client’s Faraday connection



197
198
199
# File 'lib/sec_api/client.rb', line 197

def xbrl
  @_xbrl ||= Xbrl.new(self)
end