-
Notifications
You must be signed in to change notification settings - Fork 0
Authentication
Every SDK request requires an OAuth2 bearer token (the AWS API Gateway in front of the production API rejects every unauthenticated call as of today, including conceptually-"public" market-data routes):
access-token: <40-char token>
- Log in to cryptohopper.com.
-
Developer → Create App — gives you a
client_id+client_secret. - Complete the OAuth consent flow for your app, which returns a bearer token.
Options to automate step 3:
-
The official CLI:
cryptohopper loginopens the consent page, runs a loopback listener, and persists the token to~/.cryptohopper/config.json. You can read that file from Ruby and reuse the token. -
Your own code: call the server's
/oauth2/authorize+/oauth2/tokenendpoints directly. The CLI's implementation is short (~300 lines of TypeScript) and a reasonable reference.
ch = Cryptohopper::Client.new(
api_key: ENV.fetch("CRYPTOHOPPER_TOKEN"),
app_key: ENV["CRYPTOHOPPER_APP_KEY"],
base_url: "https://api.cryptohopper.com/v1",
timeout: 30,
max_retries: 3,
user_agent: "my-app/1.0",
)The api_key: argument is required; everything else is optional.
Cryptohopper lets OAuth apps identify themselves on every request via the x-api-app-key header (value = your OAuth client_id). When set, the SDK adds the header automatically. Reasons to set it:
- Shows up in Cryptohopper's server-side telemetry — you can attribute your own traffic.
- Drives per-app rate limits — if two apps share a token, they get independent quotas.
- Harmless to omit; the server accepts unattributed requests.
Override for staging or a local dev server. The default is https://api.cryptohopper.com/v1. The trailing /v1 is part of the base; resource paths are relative to it.
ch = Cryptohopper::Client.new(
api_key: token,
base_url: "https://api.staging.cryptohopper.com/v1",
)Per-request timeout in seconds. Defaults to 30. Both connect and read timeouts share this value.
The 429-retry path may stack additional time on top of this — set it conservatively if max_retries: is high.
Number of automatic retries on HTTP 429. Default 3. Set to 0 to disable. See Rate Limits for details.
Appended after the SDK's own User-Agent (cryptohopper-sdk-ruby/<version>). Set this to identify your client to Cryptohopper support if you ever need to debug something on their side.
If your Cryptohopper app has IP allowlisting enabled, requests from unlisted IPs return 403 FORBIDDEN. The SDK surfaces this as Cryptohopper::Error with code == "FORBIDDEN" and a populated ip_address field showing the IP Cryptohopper saw:
begin
ch.hoppers.list
rescue Cryptohopper::Error => e
if e.code == "FORBIDDEN"
puts "blocked from #{e.ip_address}"
end
endFor CI where the runner IP isn't stable, either disable IP allowlisting for that app or route outbound traffic through a stable IP (NAT gateway, VPN, dedicated proxy).
Cryptohopper bearer tokens are long-lived but can be revoked:
- Manually from the dashboard.
- When the user revokes consent.
The SDK surfaces revocation as UNAUTHORIZED on the next call. There is no automatic refresh-token handling in the SDK today — if your app uses refresh tokens, handle the UNAUTHORIZED branch by exchanging your refresh token for a new access token and constructing a fresh client:
class CryptohopperWrapper
def initialize
@mutex = Mutex.new
@client = build_client(load_token)
end
def call(&block)
block.call(@client)
rescue Cryptohopper::Error => e
raise unless e.code == "UNAUTHORIZED"
@mutex.synchronize do
@client = build_client(refresh_token!)
end
block.call(@client) # retry once with the fresh client
end
private
def build_client(token)
Cryptohopper::Client.new(api_key: token)
end
endThe client's api_key is intentionally not mutable — construct a fresh client for token rotation. The cost is small and it sidesteps races where one in-flight request uses an old token while another uses the new.
Cryptohopper::Client is safe to share across threads as long as nothing mutates it after construction (none of the public surface does). One client serving a Sidekiq pool, a Puma worker pool, or a Concurrent::FixedThreadPool is fine.
require "concurrent"
pool = Concurrent::FixedThreadPool.new(8)
futures = hopper_ids.map do |id|
Concurrent::Promises.future_on(pool) { ch.hoppers.get(id) }
end
results = Concurrent::Promises.zip(*futures).value!See Rate Limits for guidance on capping concurrency at the API quota.
The AWS API Gateway in front of the production API rejects every call without a valid OAuth bearer token (returns 405 Missing Authentication Token). This holds even on routes the API conceptually treats as "public market data" like /exchange/ticker and /market/homepage. There is no way to call the SDK without a real token today.
Pages
Other SDKs
Resources