<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Igor</title>
    <description>The latest articles on DEV Community by Igor (@tigusigalpa).</description>
    <link>https://dev.to/tigusigalpa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1475742%2Fbaa73c6e-cca1-4aef-bf6a-e4c1f0ed9d5d.png</url>
      <title>DEV Community: Igor</title>
      <link>https://dev.to/tigusigalpa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tigusigalpa"/>
    <language>en</language>
    <item>
      <title>Stop Fighting Generated Code: Introducing a Hand-Crafted Go SDK for KuCoin</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Sun, 09 Aug 2026 12:58:39 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/stop-fighting-generated-code-introducing-a-hand-crafted-go-sdk-for-kucoin-4eaj</link>
      <guid>https://dev.to/tigusigalpa/stop-fighting-generated-code-introducing-a-hand-crafted-go-sdk-for-kucoin-4eaj</guid>
      <description>&lt;p&gt;Integrating an exchange API is rarely difficult because an endpoint is missing. It is difficult because the integration becomes part of a system that must behave predictably when the network is slow, when rate limits are reached, when API documentation is ambiguous, and when a number must retain every decimal place. In that environment, convenience wrappers can be expensive: a client that merely exposes every endpoint is not necessarily a client that feels safe to build upon.&lt;/p&gt;

&lt;p&gt;That is the motivation behind &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;&lt;code&gt;kucoin-go&lt;/code&gt;&lt;/a&gt;, an unofficial Go client for KuCoin’s UTA (Unified Trading Account) and Classic API families. The project is built from scratch against KuCoin’s current documentation rather than as a wrapper over the official Universal SDK. Its goal is deliberately narrower: offer a consistent, idiomatic Go experience and expand endpoint coverage method by method, with tests and documentation keeping pace. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“We'd rather ship a small, correct surface than a large, half-tested one.” — &lt;em&gt;kucoin-go&lt;/em&gt; project README &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is not a claim that generated SDKs have no place. If your application needs comprehensive KuCoin coverage immediately, the official client remains the sensible choice. &lt;code&gt;kucoin-go&lt;/code&gt; is for Go developers who prefer an explicit, thoughtfully designed API surface—and who value being able to see precisely what is implemented today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a smaller SDK can be a better foundation
&lt;/h2&gt;

&lt;p&gt;An exchange client occupies an awkward but important boundary. On one side are remote API shapes, rate limits, account models, and authentication rules. On the other are your Go services, jobs, alerting pipeline, or trading logic. A useful SDK should make that boundary visible rather than hiding it behind generic types and optimistic defaults.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kucoin-go&lt;/code&gt; applies a few clear rules at that boundary. Network calls take a &lt;code&gt;context.Context&lt;/code&gt; as their first argument, letting an application define cancellation and timeout behavior using standard Go mechanisms. The client can also receive an injected &lt;code&gt;*http.Client&lt;/code&gt;, clock, and logger, which makes it easier to apply a house network policy in production and to write controlled tests. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project keeps KuCoin’s two account models explicit. UTA and Classic expose different permissions, hosts, and response shapes, so the SDK does not flatten them into one misleading abstraction. The current UTA surface is accessed through &lt;code&gt;client.UTA&lt;/code&gt;; the Classic service root is planned for later coverage. This distinction is especially helpful when an application needs to reason about which account model a request actually belongs to. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Design choice&lt;/th&gt;
&lt;th&gt;What it means in practice&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context-first calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every network method begins with &lt;code&gt;context.Context&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Request deadlines and cancellation stay under application control.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Explicit service roots&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;UTA and Classic are modeled as separate domains.&lt;/td&gt;
&lt;td&gt;Account-specific permissions and payload differences are not blurred.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Injected dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;HTTP client, clock, logger, and retry policy are configurable.&lt;/td&gt;
&lt;td&gt;Production behavior and testing can be tailored without forking the SDK.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typed transport errors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Errors work with &lt;code&gt;errors.Is&lt;/code&gt; and &lt;code&gt;errors.As&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;Code can branch on meaningful conditions instead of matching strings.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Treat money as data, not as a float
&lt;/h2&gt;

&lt;p&gt;One of the most intentional choices in &lt;code&gt;kucoin-go&lt;/code&gt; is deceptively simple: price, quantity, PnL, and fee fields are represented as &lt;strong&gt;strings&lt;/strong&gt; at the transport boundary. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That may look less convenient than decoding directly into &lt;code&gt;float64&lt;/code&gt;, but it is the correct friction for financial values. Binary floating-point numbers cannot represent many decimal fractions exactly. If a service receives an exchange value, performs arithmetic, and serializes it again, silent rounding can turn an apparently harmless conversion into a difficult reconciliation problem.&lt;/p&gt;

&lt;p&gt;The library therefore preserves the value exactly as it arrives from the API and asks the application to choose an arithmetic type intentionally—for example, &lt;code&gt;math/big.Rat&lt;/code&gt; or a decimal package. It is a small API design decision with a useful message: precision should be an explicit responsibility, never an accidental by-product of JSON unmarshalling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transport behavior designed for production realities
&lt;/h2&gt;

&lt;p&gt;A clean Go method signature is only half of an SDK. What happens when the server rejects a call, sends a business-level error, or asks the caller to slow down matters just as much.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;kucoin-go&lt;/code&gt; uses a shared transport executor that decodes response envelopes and exposes useful metadata, including the HTTP status, KuCoin business code and message, request ID, rate-limit headers, and server timing headers. The SDK’s error hierarchy is designed to be inspected with Go’s native error helpers rather than parsed as text. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Market&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetTickers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TradeTypeSpot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"BTC-USDT"&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrRateLimited&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Apply the application's backoff strategy.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;transport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;KucoinError&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HTTPStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Its retry policy is conservative by design. It applies exponential backoff with jitter only to &lt;code&gt;GET&lt;/code&gt; requests and bounds the total retry window. Write operations are not automatically retried. That distinction is important: reissuing a data lookup after a transient failure may be safe; reissuing a request that places, cancels, or amends an order can create a duplicate-action problem. The project makes the safer default explicit and leaves idempotency decisions with the application. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authentication follows the same philosophy. The library includes an independently implemented HMAC-SHA256 signer for KuCoin authentication headers and validates it with known-answer fixture vectors. The README also recommends obtaining credentials from environment variables or a secret store, using the minimum permission required, and restricting keys by IP where the exchange supports it. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation that is checked, not merely written
&lt;/h2&gt;

&lt;p&gt;Many SDK repositories start with a good endpoint table and gradually lose the race between code changes and documentation. &lt;code&gt;kucoin-go&lt;/code&gt; tries to remove that failure mode.&lt;/p&gt;

&lt;p&gt;Its endpoint reference is generated from an internal manifest, &lt;code&gt;internal/endpoints.yaml&lt;/code&gt;. Contributors update the manifest, regenerate &lt;code&gt;docs/ENDPOINTS.md&lt;/code&gt;, and CI rejects a change if the generated file differs from the committed version. In addition, each exported method must link to the exact KuCoin documentation page that it implements. &lt;a href="https://github.com/tigusigalpa/kucoin-go/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The practical result is a highly useful question to ask before starting an integration: &lt;strong&gt;does the method exist here today?&lt;/strong&gt; Rather than inferring coverage from a roadmap or package name, a developer can consult a concise list of supported methods, their account mode, HTTP method, permission requirement, test location, and a direct upstream documentation link. &lt;a href="https://github.com/tigusigalpa/kucoin-go/blob/main/docs/ENDPOINTS.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Current scope: the honest version
&lt;/h2&gt;

&lt;p&gt;This is an early, pre-1.0 Phase 1 checkpoint. Today, &lt;code&gt;kucoin-go&lt;/code&gt; implements and tests &lt;strong&gt;UTA Market&lt;/strong&gt; functionality only. It does not yet implement UTA account, order, position, leverage, or transfer operations; Classic Spot, Margin, and Futures coverage; or any WebSocket client. &lt;a href="https://github.com/tigusigalpa/kucoin-go/blob/main/docs/ENDPOINTS.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The current coverage map contains ten UTA Market methods. Most are public read operations, while order-book retrieval is a notable exception: development smoke testing showed that &lt;code&gt;GetOrderBook&lt;/code&gt; requires authentication and the &lt;code&gt;General&lt;/code&gt; permission, despite appearing public alongside similar market-data endpoints. The SDK documents that requirement rather than letting users discover it at runtime. &lt;a href="https://github.com/tigusigalpa/kucoin-go/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Available now&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;GetInstruments&lt;/code&gt;, &lt;code&gt;GetTickers&lt;/code&gt;, &lt;code&gt;GetKlines&lt;/code&gt;, &lt;code&gt;GetTrades&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;UTA market discovery and trading-data retrieval.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;GetCurrencies&lt;/code&gt;, &lt;code&gt;GetCurrency&lt;/code&gt;, &lt;code&gt;GetServiceStatus&lt;/code&gt;, &lt;code&gt;GetAnnouncements&lt;/code&gt;, &lt;code&gt;GetTradeStatistics&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;UTA market and platform information.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GetOrderBook&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Supported, but requires credentials with the &lt;code&gt;General&lt;/code&gt; permission. &lt;a href="https://github.com/tigusigalpa/kucoin-go/blob/main/docs/ENDPOINTS.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebSockets, order placement, transfers, Classic APIs&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Not implemented yet.&lt;/strong&gt; &lt;a href="https://github.com/tigusigalpa/kucoin-go/blob/main/docs/ENDPOINTS.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That candor is a feature, not a disclaimer hidden at the bottom of the page. A library that makes its limits obvious allows developers to select it appropriately: it is ready to support UTA market-data integration and not ready to run a production order-execution workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick start: fetch a ticker without API credentials
&lt;/h2&gt;

&lt;p&gt;Public market-data calls offer the fastest way to evaluate the SDK. The repository requires Go 1.22 or later and installs with the standard command below. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/tigusigalpa/kucoin-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following example retrieves the latest price for the &lt;code&gt;BTC-USDT&lt;/code&gt; spot symbol. Because this call is public, the client can be created without credentials. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="n"&gt;kucoin&lt;/span&gt; &lt;span class="s"&gt;"github.com/tigusigalpa/kucoin-go"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/tigusigalpa/kucoin-go/uta/market"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;kucoin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UTA&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Market&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetTickers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TradeTypeSpot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"BTC-USDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example captures the intended ergonomics: the account model and domain are visible in the call path, request lifetime is represented by a context, and &lt;code&gt;LastPrice&lt;/code&gt; remains an exact string until the application decides how to process it.&lt;/p&gt;

&lt;h2&gt;
  
  
  An invitation to shape the roadmap
&lt;/h2&gt;

&lt;p&gt;The roadmap is organized around a reliable core first, then broader trading domains, then specialty capabilities such as funding, subaccounts, and additional KuCoin products. Contributions are welcome, particularly for a single missing REST endpoint accompanied by fixtures, tests, a documentation link, and an updated generated coverage entry. &lt;a href="https://github.com/tigusigalpa/kucoin-go/blob/main/CONTRIBUTING.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your project needs full API coverage now, start with the official SDK. If you need a compact Go client for UTA market data and want a codebase that favors clear constraints, typed boundaries, testability, and documentation fidelity, try &lt;code&gt;kucoin-go&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Explore the repository, inspect the coverage map before integrating, and open an issue if the next endpoint on the roadmap is the one your project needs: &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;&lt;strong&gt;github.com/tigusigalpa/kucoin-go&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;kucoin-go&lt;/code&gt; is an unofficial community-maintained client. It is provided as-is and is not financial advice. Review the project’s security notice and test new integrations with least-privilege credentials before using them with funded accounts. &lt;a href="https://github.com/tigusigalpa/kucoin-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>go</category>
      <category>kucoin</category>
      <category>cryptocurrency</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>Introducing bitget-php: a production-grade PHP and Laravel SDK for Bitget UTA v3</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Sun, 09 Aug 2026 09:21:11 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/introducing-bitget-php-a-production-grade-php-and-laravel-sdk-for-bitget-uta-v3-eh1</link>
      <guid>https://dev.to/tigusigalpa/introducing-bitget-php-a-production-grade-php-and-laravel-sdk-for-bitget-uta-v3-eh1</guid>
      <description>&lt;p&gt;PHP is a strong fit for dashboards, back-office systems, trading utilities, and event-driven services. Yet exchange integrations can still become the most fragile part of the stack. Developers must sign authenticated requests correctly, retain decimal precision, surface actionable API errors, and keep real-time connections healthy when a network or remote service interrupts them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;&lt;strong&gt;bitget-php&lt;/strong&gt;&lt;/a&gt; is an open-source SDK that aims to make that boundary more dependable. It is a production-oriented PHP 8.2+ client for the Bitget Unified Trading Account (UTA) v3 API, with optional Laravel 10–13 integration. The project’s initial release focuses deliberately on a practical core: market data, account operations, trading operations, and a reconnecting WebSocket client. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The library is built for &lt;strong&gt;demo-first trading safety&lt;/strong&gt;. Its own documentation recommends validating new code against Bitget demo credentials before connecting an application to a live account. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article introduces the library from a developer’s perspective: what it covers today, how its design avoids several common integration pitfalls, and how it can become a stable starting point for a PHP-based Bitget integration.&lt;/p&gt;

&lt;h2&gt;
  
  
  The exchange-integration problem is more than HTTP
&lt;/h2&gt;

&lt;p&gt;Calling an exchange endpoint may look like a simple &lt;code&gt;POST&lt;/code&gt; or &lt;code&gt;GET&lt;/code&gt;, but a production integration has a much wider responsibility. A client must consistently apply request signing, make error states intelligible to the application, avoid leaking API credentials through logs, and preserve the exact meaning of a decimal value. It also needs an operational model for real-time transport: what happens after a disconnect, and how does an application recover its subscriptions?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bitget-php&lt;/code&gt; addresses these concerns at the SDK layer. It uses Guzzle for HTTP transport and allows a &lt;code&gt;GuzzleHttp\Client&lt;/code&gt; to be injected, which is helpful when testing, routing requests through a proxy, or applying project-specific transport configuration. It supports PSR-3 logging while using a no-op logger by default, and its documentation states that credentials are never logged. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Design concern&lt;/th&gt;
&lt;th&gt;How &lt;code&gt;bitget-php&lt;/code&gt; addresses it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Decimal values&lt;/td&gt;
&lt;td&gt;Prices, quantities, PnL, and fee values are represented as strings rather than floats. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API failures&lt;/td&gt;
&lt;td&gt;A typed exception hierarchy exposes specific application-level failure paths and the raw Bitget error code. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP testing and customization&lt;/td&gt;
&lt;td&gt;The Guzzle client is injectable instead of being fixed internally. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability&lt;/td&gt;
&lt;td&gt;The SDK accepts a PSR-3 logger and defaults to &lt;code&gt;NullLogger&lt;/code&gt;; credentials are not written to logs. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Real-time reliability&lt;/td&gt;
&lt;td&gt;The WebSocket client maintains heartbeat handling, reconnection, and prior subscription recovery. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Framework flexibility&lt;/td&gt;
&lt;td&gt;Laravel support is supplied without making Laravel a requirement for non-Laravel projects. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The approach to decimals deserves special attention. Binary floating-point values cannot precisely represent many decimal fractions, so silently converting exchange quantities or prices into PHP floats can change a value in ways that matter to accounting and order logic. By keeping API-facing numeric values as strings, the SDK leaves exact arithmetic under the developer’s control, including the option to use BCMath or another appropriate decimal strategy.&lt;/p&gt;

&lt;h2&gt;
  
  
  A modern PHP foundation without framework lock-in
&lt;/h2&gt;

&lt;p&gt;The package requires PHP 8.2 or newer and uses strict typing throughout its codebase. It also uses readonly constructor properties, a small but important design choice that makes an object’s initialized dependencies harder to mutate accidentally. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the project offers first-class Laravel 10–13 conveniences—an auto-discovered service provider, publishable configuration, and a &lt;code&gt;Bitget&lt;/code&gt; facade—the core SDK does not have a hard &lt;code&gt;illuminate/*&lt;/code&gt; dependency outside of Laravel applications. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt; That distinction matters. A Laravel team can adopt the package with familiar configuration conventions, while a CLI worker, Symfony project, or framework-free PHP application can use the same client directly.&lt;/p&gt;

&lt;p&gt;Installation is a single Composer command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require tigusigalpa/bitget-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a Laravel application, credentials can live in configuration backed by environment variables. For a standalone service, they can be loaded through the project’s own secure configuration mechanism. In either case, API credentials should not be committed to source control or embedded into a front-end application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Phase 1 REST coverage: the useful essentials
&lt;/h2&gt;

&lt;p&gt;The project is transparent about its scope. Rather than implying that every endpoint in the Bitget UTA v3 surface is wrapped, it documents precisely which operations are included in Phase 1 and which are not. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;2&lt;/a&gt; That is a meaningful advantage for planning: teams can decide early whether the current surface matches their workflow rather than discovering a missing endpoint late in implementation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Supported SDK methods in Phase 1&lt;/th&gt;
&lt;th&gt;Typical role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Public market data&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Market::getInstruments()&lt;/code&gt;, &lt;code&gt;Market::getTickers()&lt;/code&gt;, &lt;code&gt;Market::getOrderBook()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Discover instruments, display prices, and obtain order-book data. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private account operations&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Account::getAssets()&lt;/code&gt;, &lt;code&gt;Account::getSettings()&lt;/code&gt;, &lt;code&gt;Account::setLeverage()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Read available assets and settings, then adjust leverage where supported. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Private trade operations&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Trade::placeOrder()&lt;/code&gt;, &lt;code&gt;Trade::modifyOrder()&lt;/code&gt;, &lt;code&gt;Trade::cancelOrder()&lt;/code&gt;, &lt;code&gt;Trade::getOpenOrders()&lt;/code&gt;, &lt;code&gt;Trade::getOrderHistory()&lt;/code&gt;, &lt;code&gt;Trade::getPositions()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Manage an order lifecycle and reconcile positions. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is enough to support a well-defined first version of several products: a market-monitoring page, a controlled order-entry service, a bot that watches positions, or an internal trading-operations console. The SDK’s endpoint map also links every listed method to its corresponding Bitget API documentation, helping engineers trace an integration requirement from application code back to the exchange specification. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Equally important, the repository documents the current boundaries. In Phase 1, it does not include areas such as transfers, Trading Bot, Copy Trading, RFQ, Fiat, Finance/earn, batch orders, plan/trigger orders, or the other UTA v3 REST endpoints. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;2&lt;/a&gt; A reliable promotion should say this plainly: the library is a focused foundation, not an unsupported promise of complete coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safer order code starts in the demo environment
&lt;/h2&gt;

&lt;p&gt;The project provides a direct demo-trading mode. When &lt;code&gt;demoTrading: true&lt;/code&gt;—or the &lt;code&gt;BITGET_DEMO=true&lt;/code&gt; setting—is used with Demo API credentials, REST requests include Bitget’s required &lt;code&gt;paptrading: 1&lt;/code&gt; header. The SDK also exposes demo public and private WebSocket URLs. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The following example follows the repository’s safety pattern. An order is both configured for demo trading and guarded by an explicit environment-variable opt-in. The intentionally distant limit price helps prevent an accidental fill during a test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Bitget\Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bitget.api_key'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;secretKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bitget.secret_key'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;passphrase&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bitget.passphrase'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;demoTrading&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Do not send an order unless the explicit gate is enabled.&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'BITGET_ENABLE_TRADING'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'category'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SPOT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'symbol'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTCUSDT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'side'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'buy'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'orderType'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'limit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'10000'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'qty'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'0.001'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That pattern does not replace testing, permissions management, risk limits, or review. It does, however, make the safer path easier to follow. A team can wire up application behavior in a simulated environment, exercise success and failure paths, and remove the deliberate gate only after it has made a conscious production decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typed errors make recovery logic explicit
&lt;/h2&gt;

&lt;p&gt;Exchange error handling should not collapse every failure into one generic exception. An invalid API key, a rate-limit response, insufficient funds, and an order lookup failure lead to very different recovery actions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bitget-php&lt;/code&gt; exposes typed exceptions including &lt;code&gt;AuthenticationException&lt;/code&gt;, &lt;code&gt;RateLimitException&lt;/code&gt;, &lt;code&gt;InvalidParameterException&lt;/code&gt;, &lt;code&gt;InsufficientFundsException&lt;/code&gt;, and &lt;code&gt;OrderNotFoundException&lt;/code&gt;. Each inherits from the SDK’s exception model and retains Bitget’s raw error code; a general &lt;code&gt;BitgetException&lt;/code&gt; also gives access to the raw response. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Bitget\Exceptions\AuthenticationException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Bitget\Exceptions\InsufficientFundsException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Bitget\Exceptions\RateLimitException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Bitget\Exceptions\BitgetException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;trade&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="cm"&gt;/* order payload */&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AuthenticationException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Rotate or correct credentials; do not retry blindly.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InsufficientFundsException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Notify the workflow that the required balance or margin is unavailable.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RateLimitException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Back off before retrying according to your application policy.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BitgetException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Record $e-&amp;gt;bitgetCode and $e-&amp;gt;rawResponse for diagnostics.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The value here is architectural. Application code can decide which failures are retriable, which should create an alert, and which should immediately stop a workflow. That leads to clearer observability and safer automation than a single catch-all branch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-time data with reconnection built in
&lt;/h2&gt;

&lt;p&gt;A WebSocket is often the right channel for reacting to market updates or private fill notifications, but it needs operational safeguards. The SDK’s &lt;code&gt;WebsocketClient&lt;/code&gt; uses a pluggable &lt;code&gt;ConnectionInterface&lt;/code&gt;. It ships with a synchronous &lt;code&gt;textalk/websocket&lt;/code&gt; adapter, while projects using ReactPHP, Amp, or Laravel Octane can implement their own adapter for a non-blocking event loop. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the documented shape of a public ticker subscription:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Bitget\WebsocketClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$ws&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebsocketClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;WebsocketClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DEFAULT_PUBLIC_URL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$ws&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$ws&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'instType'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SPOT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'topic'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'ticker'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'symbol'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTCUSDT'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$ws&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$push&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$push&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;listen()&lt;/code&gt; is a blocking call in the default synchronous transport. It responds to Bitget’s text-frame ping/pong heartbeat and, after an unexpected disconnection, reconnects with exponential backoff from one second to a 60-second cap before resubscribing every channel that was active. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt; This turns a raw socket connection into a more usable building block for long-running workers.&lt;/p&gt;

&lt;p&gt;For private streams, credentials can be passed to the client constructor and &lt;code&gt;connect()&lt;/code&gt; performs authentication. The repository includes a &lt;code&gt;fast-fill&lt;/code&gt; example for UTA order-fill events. The subscription and listener are otherwise channel-agnostic, meaning a developer can subscribe to other Bitget channels by supplying the appropriate payload and decoding the returned array. The documentation is careful to distinguish that flexibility from endpoint-level support: beyond &lt;code&gt;fast-fill&lt;/code&gt;, additional channel payloads are not separately wrapped or decoded by Phase 1. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A focused starting point for PHP teams
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bitget-php&lt;/code&gt; is compelling because it concentrates on the reliability details that are easy to overlook: strings for API decimal fields, strict PHP conventions, injectable transport, typed exceptions, carefully handled logs, and a WebSocket client that tries to restore its prior state after a disconnect. It also does not conceal its roadmap. The public endpoint map makes current coverage visible and invites contributions to extend it. &lt;a href="https://github.com/tigusigalpa/bitget-php/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are building a PHP or Laravel service around Bitget UTA v3, the library is worth evaluating in a demo environment first. Install it, inspect the endpoint map against your requirements, and use the documented examples to validate your authentication, error handling, and real-time design before any production rollout.&lt;/p&gt;

&lt;p&gt;Visit the project on GitHub: &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;tigusigalpa/bitget-php&lt;/a&gt;. Feedback, bug reports, and endpoint-coverage contributions are welcome through the repository. &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article is for software-development information only. It does not constitute investment, trading, or financial advice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>bitget</category>
      <category>trading</category>
    </item>
    <item>
      <title>Introducing bitget-go: An Idiomatic Go SDK for Bitget UTA v3</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Sun, 09 Aug 2026 06:33:12 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/introducing-bitget-go-an-idiomatic-go-sdk-for-bitget-uta-v3-4ceb</link>
      <guid>https://dev.to/tigusigalpa/introducing-bitget-go-an-idiomatic-go-sdk-for-bitget-uta-v3-4ceb</guid>
      <description>&lt;p&gt;Building a trading service in Go should mean spending time on execution logic, risk controls, and observability—not reimplementing request signatures, decoding generic JSON maps, or rebuilding WebSocket recovery loops for every project.&lt;/p&gt;

&lt;p&gt;That is the problem &lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;&lt;strong&gt;bitget-go&lt;/strong&gt;&lt;/a&gt; aims to solve. It is an open-source, idiomatic Go SDK for the &lt;strong&gt;Bitget Unified Trading Account (UTA) API v3&lt;/strong&gt;, providing typed REST access to essential market, account, and trading operations alongside a reconnecting WebSocket client.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The short version:&lt;/strong&gt; &lt;code&gt;bitget-go&lt;/code&gt; gives Go developers typed API models, context-aware network calls, exact string representations for financial values, and automatic WebSocket reconnection—while keeping the runtime dependency footprint intentionally small.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This post looks at why those design decisions matter and how you can make your first request or stream market data in just a few lines of Go.&lt;/p&gt;




&lt;h2&gt;
  
  
  The friction behind a “simple” exchange integration
&lt;/h2&gt;

&lt;p&gt;A raw exchange API integration tends to accumulate infrastructure code quickly. Authentication must match the exchange’s HMAC signing rules; query parameters and request bodies must be encoded exactly; remote errors need useful application-level handling; and persistent streaming connections need recovery after the inevitable network interruption. None of those concerns is the business logic of a trading system, but all of them can affect its reliability.&lt;/p&gt;

&lt;p&gt;There is also a deceptively important data-modeling concern: &lt;strong&gt;numeric precision&lt;/strong&gt;. Exchange payloads contain prices, quantities, PnL, and fees. Converting decimal values directly into binary &lt;code&gt;float64&lt;/code&gt; can introduce representation artifacts. For a trading application, it is often safer to preserve the exact wire value and perform arithmetic with &lt;code&gt;math/big.Rat&lt;/code&gt; or a decimal package selected by the application.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;bitget-go&lt;/code&gt; treats those operational details as first-class SDK responsibilities. It returns monetary and quantity fields as strings, exposes typed models rather than &lt;code&gt;interface{}&lt;/code&gt;, and takes &lt;code&gt;context.Context&lt;/code&gt; as the first argument of every network call.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Common integration concern&lt;/th&gt;
&lt;th&gt;How &lt;code&gt;bitget-go&lt;/code&gt; addresses it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request authentication&lt;/td&gt;
&lt;td&gt;Signs REST requests and sets the required headers internally.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precision-sensitive values&lt;/td&gt;
&lt;td&gt;Keeps prices, quantities, PnL, and fees as strings instead of coercing them to &lt;code&gt;float64&lt;/code&gt;.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cancellations and deadlines&lt;/td&gt;
&lt;td&gt;Accepts &lt;code&gt;context.Context&lt;/code&gt; on every network call.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response parsing&lt;/td&gt;
&lt;td&gt;Uses typed models and a generic &lt;code&gt;models.BitgetResponse[T]&lt;/code&gt; envelope.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection interruptions&lt;/td&gt;
&lt;td&gt;Reconnects WebSockets with exponential backoff and restores subscriptions.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testability&lt;/td&gt;
&lt;td&gt;Allows a custom &lt;code&gt;*http.Client&lt;/code&gt; to be injected through functional options.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The result is not a wrapper that hides Go’s standard idioms. Instead, it makes them central to the API.&lt;/p&gt;




&lt;h2&gt;
  
  
  A deliberately Go-native client
&lt;/h2&gt;

&lt;p&gt;The SDK’s public surface is designed around conventions Go developers already expect. A REST client is constructed once, services are grouped by responsibility, and methods return typed values plus an &lt;code&gt;error&lt;/code&gt;. If a caller cancels a context or its deadline expires, the cancellation flows through the request instead of being concealed behind a custom concurrency abstraction.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The package also favors a lightweight dependency profile. According to its README, &lt;code&gt;gorilla/websocket&lt;/code&gt; is the only required runtime dependency; &lt;code&gt;stretchr/testify&lt;/code&gt; is used for tests.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; This is a sensible default for services where transparent dependency trees, quick builds, and straightforward vendoring matter.&lt;/p&gt;

&lt;p&gt;Installation requires Go 1.21 or later and follows the usual Go module workflow:&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/tigusigalpa/bitget-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Your first REST request
&lt;/h3&gt;

&lt;p&gt;The following example asks for the spot BTC/USDT ticker. Notice what is absent from the application: no manually constructed signature, no hand-written JSON response struct, and no &lt;code&gt;float64&lt;/code&gt; conversion for the reported last price.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;

    &lt;span class="n"&gt;bitget&lt;/span&gt; &lt;span class="s"&gt;"github.com/tigusigalpa/bitget-go"&lt;/span&gt;
    &lt;span class="s"&gt;"github.com/tigusigalpa/bitget-go/models"&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_SECRET_KEY"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_PASSPHRASE"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Market&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetTickers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CategorySpot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BTC/USDT last price: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tickers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;LastPrice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client signs the request, applies the relevant headers, parses the exchange response, and delivers typed ticker data to the caller.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; For a production service, replace &lt;code&gt;context.Background()&lt;/code&gt; with a context that has an intentional deadline, so an obsolete request does not outlive the trading decision that initiated it.&lt;/p&gt;




&lt;h2&gt;
  
  
  REST coverage for the first phase
&lt;/h2&gt;

&lt;p&gt;The project is candid about its current scope: &lt;strong&gt;Phase 1&lt;/strong&gt; focuses on core REST services plus WebSockets rather than claiming total endpoint coverage. That makes the package particularly useful for projects that need a strong foundation for market observation, account state, and order lifecycle management today.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;Available methods in Phase 1&lt;/th&gt;
&lt;th&gt;Typical application use&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Market&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GetInstruments&lt;/code&gt;, &lt;code&gt;GetTickers&lt;/code&gt;, &lt;code&gt;GetOrderBook&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Discover instruments, display prices, evaluate liquidity.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Account&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GetAssets&lt;/code&gt;, &lt;code&gt;GetSettings&lt;/code&gt;, &lt;code&gt;SetLeverage&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Read balances and configuration; update leverage where appropriate.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Trade&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PlaceOrder&lt;/code&gt;, &lt;code&gt;ModifyOrder&lt;/code&gt;, &lt;code&gt;CancelOrder&lt;/code&gt;, &lt;code&gt;GetOpenOrders&lt;/code&gt;, &lt;code&gt;GetOrderHistory&lt;/code&gt;, &lt;code&gt;GetPositions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Implement an order workflow and monitor its outcome.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The repository’s endpoint documentation is the authoritative place to check precise paths, request parameters, and the status of individual models before you depend on an endpoint in production.&lt;a href="https://github.com/tigusigalpa/bitget-go/blob/main/docs/endpoints.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  WebSockets that recover instead of merely connect
&lt;/h2&gt;

&lt;p&gt;REST is ideal for commands and snapshots; it is not the preferred path for a real-time ticker or execution feed. A reliable streaming client needs to plan for unexpected disconnections. &lt;code&gt;bitget-go&lt;/code&gt; provides both public and private WebSocket clients and exposes incoming pushes through Go channels.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a minimal public ticker subscription:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;ws&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewPublicWSClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;pushes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ws&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WSArg&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;InstType&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"SPOT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Topic&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"ticker"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Symbol&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="s"&gt;"BTCUSDT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;push&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;pushes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;push&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For authenticated streams, create the client with &lt;code&gt;NewPrivateWSClient(apiKey, secretKey, passphrase)&lt;/code&gt;; authentication occurs during &lt;code&gt;Connect&lt;/code&gt;.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; On an unexpected disconnect, the implementation retries with an exponential backoff beginning at one second and capped at 60 seconds, then resubscribes to channels opened earlier.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; The Phase 1 private channel is &lt;code&gt;fast-fill&lt;/code&gt;; the same subscription and raw-data shape can be used for other available channels as the project expands.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That recovery behavior is especially valuable in long-running worker processes. It reduces the amount of state-reconciliation code each consumer has to write, while still letting the application decide how it should process, persist, or validate the messages it receives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Errors that work with the standard library
&lt;/h2&gt;

&lt;p&gt;A good Go SDK should not force a bespoke error framework on its users. &lt;code&gt;bitget-go&lt;/code&gt; returns regular Go errors: callers can use &lt;code&gt;errors.Is&lt;/code&gt; with the package’s sentinel errors and &lt;code&gt;errors.As&lt;/code&gt; to access a typed &lt;code&gt;*BitgetError&lt;/code&gt; containing an API error code and message.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetAssets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrUnauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"authentication failed — check credentials"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BitgetError&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bitget error %s: %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"request failed: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This small distinction improves operational handling. Authentication failures can be surfaced to configuration management, exchange business errors can be logged with their code, and network or timeout errors can follow a retry policy appropriate to the application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Start with demo trading, not real funds
&lt;/h2&gt;

&lt;p&gt;Trading code deserves a careful rollout. The SDK supports Bitget demo trading through &lt;code&gt;bitget.WithDemoTrading()&lt;/code&gt;, which sends the &lt;code&gt;paptrading: 1&lt;/code&gt; header with REST requests.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; The included REST example additionally gates a trading action behind both &lt;code&gt;BITGET_DEMO=1&lt;/code&gt; and &lt;code&gt;BITGET_ENABLE_TRADING=1&lt;/code&gt;, helping prevent accidental execution while exploring the codebase.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_SECRET_KEY"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_PASSPHRASE"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithDemoTrading&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Use demo API credentials when demo mode is enabled. The project documentation warns that combining demo mode with production credentials will fail.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The client is also configurable without turning its constructor into a long, brittle parameter list. Functional options support a custom HTTP client, REST base URL, timeout, logger, locale, and demo mode.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; Injecting an &lt;code&gt;*http.Client&lt;/code&gt; is particularly useful for tracing, proxies, custom TLS transport, and &lt;code&gt;httptest&lt;/code&gt;-based unit tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_API_KEY"&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_SECRET_KEY"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BITGET_PASSPHRASE"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithHTTPClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Timeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;   &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Transport&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;myProxyTransport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;bitget&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithDemoTrading&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository provides offline unit tests with &lt;code&gt;go test ./...&lt;/code&gt; and optional demo-environment integration tests with &lt;code&gt;go test -tags=integration ./...&lt;/code&gt; once demo credentials are configured.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A practical foundation for Go trading systems
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;bitget-go&lt;/code&gt; does not attempt to prescribe a strategy, a database, or a trading architecture. Its value is more focused: it handles the exchange-integration mechanics so your application can keep ownership of the decisions that truly belong to it—risk limits, position sizing, persistence, monitoring, and execution policy.&lt;/p&gt;

&lt;p&gt;The project is released under the MIT License and welcomes contributions, bug reports, and endpoint additions.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; If your stack also includes PHP or Laravel services, the author maintains a corresponding &lt;a href="https://github.com/tigusigalpa/bitget-php" rel="noopener noreferrer"&gt;&lt;code&gt;bitget-php&lt;/code&gt;&lt;/a&gt; SDK.&lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are building Bitget UTA v3 integrations in Go, clone the repository, start with the runnable REST and WebSocket examples, and validate your workflow against demo credentials before any production rollout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/tigusigalpa/bitget-go" rel="noopener noreferrer"&gt;github.com/tigusigalpa/bitget-go&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This SDK is not affiliated with Bitget. Trading involves risk; test carefully, protect API credentials, and never commit secrets to source control.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>go</category>
      <category>bitget</category>
      <category>cryptocurrency</category>
      <category>api</category>
    </item>
    <item>
      <title>Building Robust Crypto Data Pipelines in PHP: Introducing the Token Terminal SDK</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Sun, 09 Aug 2026 06:05:28 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/building-robust-crypto-data-pipelines-in-php-introducing-the-token-terminal-sdk-21hk</link>
      <guid>https://dev.to/tigusigalpa/building-robust-crypto-data-pipelines-in-php-introducing-the-token-terminal-sdk-21hk</guid>
      <description>&lt;p&gt;The cryptocurrency and decentralized finance ecosystems generate an overwhelming amount of data every single day. For developers building financial dashboards, algorithmic trading tools, or market research platforms, accessing clean, standardized, and reliable data is absolutely critical. Token Terminal has established itself as a premier provider of fundamental financial data for the crypto space, offering institutional-grade metrics across various blockchains and decentralized applications &lt;a href="https://tokenterminal.com/docs/api-reference/introduction" rel="noopener noreferrer"&gt;1&lt;/a&gt;. However, integrating complex third-party APIs into enterprise PHP applications often requires writing significant amounts of boilerplate code to handle edge cases, rate limits, and unexpected response structures.&lt;/p&gt;

&lt;p&gt;To solve this problem and streamline the developer experience, the PHP community now has access to a dedicated solution: the &lt;code&gt;tokenterminal-php&lt;/code&gt; SDK. This new open-source package provides a robust, fully-typed, and developer-friendly PHP 8.1+ client for the Token Terminal API v2 &lt;a href="https://github.com/tigusigalpa/tokenterminal-php" rel="noopener noreferrer"&gt;2&lt;/a&gt;. Designed with modern PHP standards and framework integration in mind, it abstracts away the complexities of the underlying HTTP transport, allowing developers to focus entirely on building their applications rather than wrestling with API mechanics.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge of Integrating Financial APIs
&lt;/h2&gt;

&lt;p&gt;When working with comprehensive financial data APIs like Token Terminal, developers frequently encounter several architectural challenges. First, there is the issue of rate limiting. Token Terminal enforces a strict limit of 1,000 requests per minute &lt;a href="https://tokenterminal.com/docs/api-reference/best-practices" rel="noopener noreferrer"&gt;3&lt;/a&gt;. When building data pipelines that ingest historical metrics across hundreds of assets, hitting this limit is practically guaranteed. A naive implementation will simply crash or drop data, requiring manual intervention.&lt;/p&gt;

&lt;p&gt;Second, the cryptocurrency space moves rapidly. Projects frequently rebrand, merge, or migrate to new smart contracts. The Token Terminal API handles this gracefully by issuing HTTP 308 Permanent Redirects when a requested project ID has been renamed &lt;a href="https://tokenterminal.com/docs/api-reference/best-practices" rel="noopener noreferrer"&gt;3&lt;/a&gt;. However, standard HTTP clients often require explicit configuration to follow these redirects correctly while preserving the original request context and authentication headers.&lt;/p&gt;

&lt;p&gt;Finally, there is the challenge of partial success. When requesting data for multiple metrics simultaneously, some metric IDs might be valid while others are deprecated or misspelled. A rigid API client might throw an exception and discard the entire response, forcing the developer to parse raw JSON to salvage the valid data. Building a robust client that gracefully handles these scenarios requires careful architectural planning and extensive testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the Token Terminal PHP SDK
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;tokenterminal-php&lt;/code&gt; SDK was created specifically to address these integration challenges while providing a fluent, modern PHP interface. It acts as a comprehensive bridge between your PHP application and the Token Terminal infrastructure, ensuring that your data pipelines remain resilient and maintainable &lt;a href="https://github.com/tigusigalpa/tokenterminal-php" rel="noopener noreferrer"&gt;2&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comprehensive Endpoint Coverage
&lt;/h3&gt;

&lt;p&gt;One of the primary strengths of the SDK is its complete coverage of the Token Terminal API v2. It supports all 24 documented endpoints out of the box. Whether you need to fetch a list of supported market sectors, retrieve deep financial statements for a specific decentralized protocol, or access specialized datasets like the crypto screener and insider transactions, the SDK provides a dedicated, strongly-typed method for the job.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API Domain&lt;/th&gt;
&lt;th&gt;Available Data&lt;/th&gt;
&lt;th&gt;Example SDK Method&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Assets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Individual token metrics and historical data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$client-&amp;gt;assets()-&amp;gt;historicalMetrics($id, $req)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Projects&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protocol financial statements and aggregations&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$client-&amp;gt;projects()-&amp;gt;financialStatement($id, $req)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Market Sectors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Categorized industry segments&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$client-&amp;gt;marketSectors()-&amp;gt;all()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Specific data points across the ecosystem&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$client-&amp;gt;metrics()-&amp;gt;data($id, $req)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Datasets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pre-compiled research and screening data&lt;/td&gt;
&lt;td&gt;&lt;code&gt;$client-&amp;gt;datasets()-&amp;gt;cryptoScreener($req)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Built for Resilience and Reliability
&lt;/h3&gt;

&lt;p&gt;Reliability is paramount when dealing with financial data. The SDK implements sophisticated retry logic to ensure that transient network issues or rate limits do not disrupt your application flow. When the client encounters an HTTP 429 Too Many Requests response, or a 5xx server error, it automatically initiates a retry sequence using exponential backoff and jitter &lt;a href="https://github.com/tigusigalpa/tokenterminal-php" rel="noopener noreferrer"&gt;2&lt;/a&gt;. Furthermore, it natively respects the &lt;code&gt;Retry-After&lt;/code&gt; header provided by the Token Terminal API, ensuring that your application waits exactly as long as required before attempting the request again.&lt;/p&gt;

&lt;p&gt;This resilience extends to how the SDK handles the aforementioned 308 redirects. If a project undergoes a rebranding and its identifier changes, the SDK transparently follows the redirect, retrieves the data using the new identifier, and returns the result to your application without requiring any code changes on your end.&lt;/p&gt;

&lt;h3&gt;
  
  
  Graceful Handling of Partial Success
&lt;/h3&gt;

&lt;p&gt;Perhaps one of the most developer-friendly features of the &lt;code&gt;tokenterminal-php&lt;/code&gt; package is its approach to partial success responses. When querying multiple metrics, Token Terminal may return valid data alongside an array of errors for the invalid parameters. Instead of throwing a generic exception and discarding the payload, the SDK encapsulates the response in an immutable &lt;code&gt;TokenTerminalResult&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;This object allows developers to easily access both the successful data payload and the specific error details. You can iterate through the valid data to populate your database while simultaneously logging the errors for the invalid metric IDs, ensuring zero data loss during complex batch operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started with the SDK
&lt;/h2&gt;

&lt;p&gt;Integrating the SDK into your project is straightforward. Because it relies on the PSR-18 standard for HTTP clients, it is highly decoupled and framework-agnostic. While Guzzle is provided as the default transport, you can easily substitute it with any PSR-18 compatible client of your choosing.&lt;/p&gt;

&lt;p&gt;Installation is handled via Composer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require tigusigalpa/tokenterminal-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, initializing the client requires nothing more than your API key. You can instantiate it directly from your environment variables or build a custom configuration object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\TokenTerminal\TokenTerminalClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Initialize the client using your API key&lt;/span&gt;
&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TokenTerminalClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'your-api-key'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Fetch all supported projects and iterate through the results&lt;/span&gt;
&lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$project&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$project&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;' (ID: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$project&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'project_id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;")&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The configuration architecture is entirely immutable. If you need to adjust timeout settings or modify the retry behavior for a specific task, you can use the fluent &lt;code&gt;with*()&lt;/code&gt; methods to generate a new configuration instance without altering the global state of your application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exception Handling
&lt;/h3&gt;

&lt;p&gt;The SDK provides a granular exception hierarchy, allowing developers to catch and handle specific HTTP errors cleanly. Instead of parsing status codes manually, you can catch &lt;code&gt;UnauthorizedException&lt;/code&gt; for invalid keys, &lt;code&gt;RateLimitException&lt;/code&gt; for quota issues, or a general &lt;code&gt;ApiException&lt;/code&gt; as a fallback.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\TokenTerminal\Exceptions\RateLimitException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\TokenTerminal\Exceptions\NotFoundException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'uniswap'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RateLimitException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// The SDK handles retries automatically, but if max attempts are exceeded:&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Rate limit exceeded. Try again after: "&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRetryAfter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;NotFoundException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Project not found in the Token Terminal registry."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  First-Class Laravel Integration
&lt;/h2&gt;

&lt;p&gt;While the SDK is perfectly suited for vanilla PHP applications, it truly shines when integrated into the Laravel ecosystem. The package includes auto-discovery, meaning the service provider and facade are registered automatically upon installation.&lt;/p&gt;

&lt;p&gt;Laravel developers can publish the configuration file to their &lt;code&gt;config&lt;/code&gt; directory and manage their API credentials directly through the standard &lt;code&gt;.env&lt;/code&gt; file. Once configured, accessing the Token Terminal API becomes as simple as calling the facade from anywhere in your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\TokenTerminal\Laravel\Facades\TokenTerminal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Fetch the revenue breakdown for Uniswap&lt;/span&gt;
&lt;span class="nv"&gt;$metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TokenTerminal&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;projects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;metricAggregations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'uniswap'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This zero-configuration approach significantly reduces the time to market for Laravel-based financial applications and analytics dashboards.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building reliable data pipelines in the cryptocurrency space requires tools that can handle the unique challenges of the ecosystem. The &lt;code&gt;tokenterminal-php&lt;/code&gt; SDK provides PHP developers with a powerful, resilient, and elegant solution for integrating Token Terminal's comprehensive financial data into their applications. By abstracting away rate limits, redirects, and complex error handling, it allows you to focus on extracting insights and delivering value to your users.&lt;/p&gt;

&lt;p&gt;If you are building data-driven applications in PHP, we highly encourage you to explore the package. You can view the source code, read the extensive documentation, and contribute to the project on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explore the repository:&lt;/strong&gt; &lt;a href="https://github.com/tigusigalpa/tokenterminal-php" rel="noopener noreferrer"&gt;tigusigalpa/tokenterminal-php on GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>tokenterminal</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>Build Resilient Web3 Data Pipelines in Go with tokenterminal-go</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Wed, 05 Aug 2026 12:32:12 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/build-resilient-web3-data-pipelines-in-go-with-tokenterminal-go-1lef</link>
      <guid>https://dev.to/tigusigalpa/build-resilient-web3-data-pipelines-in-go-with-tokenterminal-go-1lef</guid>
      <description>&lt;p&gt;When a Go application needs on-chain and protocol-level data, the HTTP request itself is usually the easy part. The difficult work starts afterward: defining request models, decoding inconsistent payloads, respecting rate limits, recovering from transient failures, and deciding what to do when one part of a multi-metric query succeeds while another part does not. Those concerns can quietly turn a small integration into a maintenance burden.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;code&gt;tokenterminal-go&lt;/code&gt;&lt;/strong&gt;&lt;/a&gt; is an open-source, production-oriented Go SDK for Token Terminal API v2 that aims to remove that plumbing. The project supports all 24 documented API routes across Assets, Projects, Market Sectors, Metrics, and Datasets; it requires Go 1.21 or newer and uses only the Go standard library. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; It is a focused choice for engineers building internal analytics services, data jobs, dashboards, research tooling, or any application that needs Token Terminal data without hand-rolling an HTTP client.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The practical promise:&lt;/strong&gt; keep the integration idiomatic and type-aware, while the client handles the failure modes that normally appear only after an application reaches real traffic.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why an SDK matters here
&lt;/h2&gt;

&lt;p&gt;Token Terminal’s API gives programmatic access to its data, but it requires an API key and an API-enabled plan. &lt;a href="https://tokenterminal.com/docs/api-reference/introduction" rel="noopener noreferrer"&gt;2&lt;/a&gt; That makes the client layer part of the application’s operational surface: it needs to handle credentials, request timeouts, rate limits, pagination or filtering parameters where relevant, and failures that should not crash a larger data pipeline.&lt;/p&gt;

&lt;p&gt;The library addresses these needs with a small, deliberate design. Its client methods take a &lt;code&gt;context.Context&lt;/code&gt;, its response envelopes use generic &lt;code&gt;Result[T]&lt;/code&gt; types, and its errors can be inspected with standard Go mechanisms such as &lt;code&gt;errors.Is&lt;/code&gt; and &lt;code&gt;errors.As&lt;/code&gt;. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; In other words, callers can keep control of cancellation and business policy instead of receiving opaque, string-only errors.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;What it means in practice&lt;/th&gt;
&lt;th&gt;Why it is useful&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Zero external dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The package uses the standard library rather than adding third-party runtime packages. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;A smaller dependency surface makes the SDK easier to audit, vendor, and upgrade.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type-safe API models&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Typed request structures and generic &lt;code&gt;Result[T]&lt;/code&gt; envelopes are used across the client. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Editors provide better completion, and more mistakes are caught before a request is sent.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Context-aware calls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every client method accepts &lt;code&gt;context.Context&lt;/code&gt;. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;A service can enforce deadlines or stop in-flight work when a request is cancelled.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Retry with backoff&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GET requests can be retried for rate limiting, server errors, and transient network failures. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Temporary failures are less likely to become application-visible outages.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Partial-success preservation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Valid &lt;code&gt;data&lt;/code&gt; and API-supplied &lt;code&gt;errors&lt;/code&gt; are both retained in the result. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;A single invalid metric does not have to discard all usable data from the same response.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Concurrency-safe client&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;One &lt;code&gt;Client&lt;/code&gt; may be shared safely across goroutines. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Parallel collection jobs do not need to create a separate client for each worker.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A fast path from API key to useful data
&lt;/h2&gt;

&lt;p&gt;Installing the package follows the normal Go workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/tigusigalpa/tokenterminal-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client can be created with an API key, environment-driven configuration, or functional options. The repository documents options for the base URL, a custom HTTP client, timeout, retry count and delay, User-Agent, and opt-in POST retries. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; The following example is adapted from the project’s historical-metrics example. It requests Uniswap fees and revenue on Ethereum for a specified time range, then prints any partial issues rather than throwing away the successful data. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go/blob/main/examples/project_metrics/main.go" rel="noopener noreferrer"&gt;3&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="n"&gt;tt&lt;/span&gt; &lt;span class="s"&gt;"github.com/tigusigalpa/tokenterminal-go"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TOKEN_TERMINAL_API_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"TOKEN_TERMINAL_API_KEY is not set"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;15&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"create client: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2025-01-01"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"2025-01-31"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HistoricalMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="s"&gt;"uniswap"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HistoricalMetricsParams&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;MetricIDs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"fees"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"revenue"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;ChainIDs&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"ethereum"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="n"&gt;Start&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;          &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;End&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;OrderDirection&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OrderAscending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatalf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"load historical metrics: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"received %d data points&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Errors&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"partial issue: %s %s=%s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;issue&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the essential advantage of the SDK: the application code describes the question—&lt;em&gt;which project, which chain, which metrics, which dates&lt;/em&gt;—rather than manually assembling URLs and decoding generic maps. The parameter structure makes filters explicit, while the &lt;code&gt;Result&lt;/code&gt; object gives callers access to both the returned data and granular API feedback. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go/blob/main/examples/project_metrics/main.go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Coverage without a maze of wrappers
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;tokenterminal-go&lt;/code&gt; does not stop at a single “get metrics” helper. The repository’s endpoint coverage map groups the 24 routes into five service areas. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; That makes the SDK suitable for broader workflows that need reference data, detailed time series, aggregates, and curated datasets in the same Go codebase.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service area&lt;/th&gt;
&lt;th&gt;Representative SDK methods&lt;/th&gt;
&lt;th&gt;Typical use case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Assets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;client.Assets.List&lt;/code&gt;, &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;HistoricalMetrics&lt;/code&gt;, &lt;code&gt;MetricsBreakdown&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Discover assets and examine an asset’s historical or aggregated metrics. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Projects&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;client.Projects.List&lt;/code&gt;, &lt;code&gt;Get&lt;/code&gt;, &lt;code&gt;HistoricalMetrics&lt;/code&gt;, &lt;code&gt;FinancialStatement&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Build protocol research pages, compare projects, or load financial statement data. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Market Sectors&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;client.MarketSectors.List&lt;/code&gt;, &lt;code&gt;Get&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Organize projects and assets by market sector. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Metrics&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;client.Metrics.List&lt;/code&gt;, &lt;code&gt;Data&lt;/code&gt;, &lt;code&gt;Aggregations&lt;/code&gt;, &lt;code&gt;Breakdown&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Query available metrics and retrieve detailed or summarized observations. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Datasets&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;client.Datasets.CryptoScreener&lt;/code&gt;, &lt;code&gt;CohortAnalysis&lt;/code&gt;, &lt;code&gt;TrendingContracts&lt;/code&gt;, and others&lt;/td&gt;
&lt;td&gt;Start with curated screens and specialized analytical datasets. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This breadth matters because application needs evolve. A first version of a dashboard may list projects and draw one time series. A later version may need a screener, an aggregation view, or a financial-statement endpoint. With the same client abstraction across these areas, the transition does not require introducing a second API integration pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reliability is a feature, not an afterthought
&lt;/h2&gt;

&lt;p&gt;A resilient client should have predictable behavior under pressure. By default, &lt;code&gt;tokenterminal-go&lt;/code&gt; retries GET requests after HTTP 429 responses, 5xx responses, and transient network errors. It uses capped exponential backoff with jitter and honors a server-provided &lt;code&gt;Retry-After&lt;/code&gt; header. POST retries are intentionally disabled unless the application explicitly enables them with &lt;code&gt;WithRetryPOST()&lt;/code&gt;. Context cancellation stops retry waits immediately. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That policy is a strong default for data retrieval: reads are commonly safe to retry, while automatic retries of requests that may change server state deserve an explicit decision. It is also aligned with Token Terminal’s documentation, which calls out HTTP 429 as the rate-limit status that clients should handle. &lt;a href="https://tokenterminal.com/docs/api-reference/best-practices" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The error model is equally practical. Instead of forcing consumers to compare error strings, the SDK exports sentinel values such as &lt;code&gt;ErrUnauthorized&lt;/code&gt;, &lt;code&gt;ErrNotFound&lt;/code&gt;, and &lt;code&gt;ErrRateLimited&lt;/code&gt;, plus an &lt;code&gt;*APIError&lt;/code&gt; that exposes structured details including status code, message, and retry information. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; A caller can therefore implement a clear policy without coupling its business logic to the client’s internal wording.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Projects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrUnauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="c"&gt;// Refresh configuration or surface a credential error.&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"check the Token Terminal API key"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrRateLimited&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIError&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"rate limited; retry after %s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RetryAfter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrNotFound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"project, asset, or metric was not found"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Token Terminal request failed: %v"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK also follows HTTP 308 redirects, which the project documents as a way to handle project or asset renames transparently. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; That is the kind of edge case developers rarely enjoy discovering after a production identifier changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preserve partial results instead of losing a whole response
&lt;/h2&gt;

&lt;p&gt;One of the more thoughtful details in &lt;code&gt;tokenterminal-go&lt;/code&gt; is its treatment of partial success. Some Token Terminal responses can contain valid data alongside an &lt;code&gt;errors&lt;/code&gt; array. The SDK keeps both. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; This matters when a query asks for several metrics, chains, or entities: one unsupported input should not automatically erase the observations that were returned successfully.&lt;/p&gt;

&lt;p&gt;A production workflow can turn this into a useful policy. Persist &lt;code&gt;result.Data&lt;/code&gt;, emit structured logs or metrics for &lt;code&gt;result.Errors&lt;/code&gt;, and alert only when the missing values break a required business rule. This approach is more robust than treating every non-empty error array as a total failure, and it gives downstream consumers a transparent view of data completeness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pair the SDK with Token Terminal’s API guidance
&lt;/h2&gt;

&lt;p&gt;The SDK gives Go applications a sound transport and type layer; efficient data architecture is still the caller’s responsibility. Token Terminal recommends maintaining an up-to-date cache or index for &lt;code&gt;/projects&lt;/code&gt; and &lt;code&gt;/metrics&lt;/code&gt;, refreshing it daily or weekly according to the application’s needs. &lt;a href="https://tokenterminal.com/docs/api-reference/best-practices" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Maintain an up-to-date cache of projects and metrics.” — Token Terminal API best practices &lt;a href="https://tokenterminal.com/docs/api-reference/best-practices" rel="noopener noreferrer"&gt;4&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That guidance fits naturally with &lt;code&gt;tokenterminal-go&lt;/code&gt;. Fetch the project and metric catalogs on a schedule that matches your product, store them in your preferred cache or database, and use those local records to validate user-selected identifiers before issuing more focused API calls. The SDK intentionally does not impose an invisible persistent cache; the repository documents an optional integration point so an application can choose its own caching strategy. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For aggregate questions, Token Terminal also advises using the Breakdown API instead of first downloading a time series and aggregating it client-side. According to the official guidance, that can reduce transferred data and improve response time. &lt;a href="https://tokenterminal.com/docs/api-reference/best-practices" rel="noopener noreferrer"&gt;4&lt;/a&gt; In the SDK, the relevant methods are exposed as &lt;code&gt;client.Metrics.Breakdown&lt;/code&gt; and &lt;code&gt;client.Assets.MetricsBreakdown&lt;/code&gt;, so the optimization is available without abandoning the same typed client model. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for normal Go engineering practices
&lt;/h2&gt;

&lt;p&gt;The project includes dedicated examples for basics, historical project metrics, metric breakdowns, datasets, error handling, and concurrent calls. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; It also documents conventional validation commands, including &lt;code&gt;go test ./... -v&lt;/code&gt;, &lt;code&gt;go test -race ./...&lt;/code&gt;, &lt;code&gt;gofmt -l .&lt;/code&gt;, and &lt;code&gt;go vet ./...&lt;/code&gt;. Its tests use &lt;code&gt;httptest.Server&lt;/code&gt; rather than calling the live Token Terminal API. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; That is a welcome design choice for teams that want deterministic tests and no hidden network dependency in their CI pipelines.&lt;/p&gt;

&lt;p&gt;The package is published under the MIT license, so it is straightforward to evaluate and incorporate into an appropriate open-source or commercial Go project. &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Give your Go code the API client it deserves
&lt;/h2&gt;

&lt;p&gt;Reliable Web3 analytics infrastructure is not just about obtaining the right endpoint. It is about handling latency, retries, cancellation, redirects, structured errors, partial results, and concurrent workload patterns without distracting from the product you are actually building.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tokenterminal-go&lt;/code&gt; packages those operational details into a concise, zero-dependency SDK for Token Terminal API v2. If your Go service needs project metrics, asset data, sector information, or specialized datasets, it is worth exploring the repository, running the examples, and adapting the client to your own pipeline.&lt;/p&gt;

&lt;p&gt;Start here: &lt;a href="https://github.com/tigusigalpa/tokenterminal-go" rel="noopener noreferrer"&gt;github.com/tigusigalpa/tokenterminal-go&lt;/a&gt;. If the SDK saves your team implementation time, consider starring the project, opening an issue with feedback, or contributing an improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>go</category>
      <category>bitcoin</category>
      <category>tokenterminal</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>Stop Hand-Rolling HTTP Clients for Glassnode: Meet glassnode-go</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Fri, 31 Jul 2026 11:50:23 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/stop-hand-rolling-http-clients-for-glassnode-meet-glassnode-go-50do</link>
      <guid>https://dev.to/tigusigalpa/stop-hand-rolling-http-clients-for-glassnode-meet-glassnode-go-50do</guid>
      <description>&lt;p&gt;Building with on-chain data is exciting right up until the integration work begins. A dashboard, research tool, alerting service, or backtesting pipeline may start with one metric, but it rarely stops there. Soon you are assembling request URLs by hand, decoding JSON into &lt;code&gt;map[string]interface{}&lt;/code&gt;, checking every response for rate-limit headers, and adding retry logic that you hope will not become the next production incident.&lt;/p&gt;

&lt;p&gt;That glue code is necessary, but it is not where a product becomes valuable. &lt;strong&gt;&lt;code&gt;glassnode-go&lt;/code&gt;&lt;/strong&gt; is designed to move that plumbing out of the way. It is an &lt;strong&gt;unofficial, community-built Go SDK for the Glassnode Basic API&lt;/strong&gt;, aimed at developers who want a more idiomatic and production-oriented integration layer. It is not affiliated with or endorsed by Glassnode; it is a focused tool for teams that would rather build with on-chain data than continually maintain HTTP wrappers around it. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“A dependency-light, production-oriented Go module for the Glassnode Basic API, built with the kind of care you'd want from a library you depend on every day.” — the &lt;code&gt;glassnode-go&lt;/code&gt; project README &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem is not fetching a number—it's operating the integration
&lt;/h2&gt;

&lt;p&gt;A raw API integration can fetch a price just fine. The difficult part begins when the application has to do it reliably: propagate cancellations through &lt;code&gt;context.Context&lt;/code&gt;, protect the API key, distinguish bad input from an exhausted quota, tune timeouts, and retrieve multiple metrics concurrently without turning the client layer into a fragile tangle of helpers.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;glassnode-go&lt;/code&gt; approaches that problem with a deliberately Go-native design. The module targets &lt;strong&gt;Go 1.21+&lt;/strong&gt; and declares no third-party module dependencies, keeping the integration limited to the standard library rather than adding transitive packages to the project. &lt;a href="https://github.com/tigusigalpa/glassnode-go/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;1&lt;/a&gt; Its client uses functional options for configuration, supports custom HTTP transports, and is documented as safe for concurrent use across goroutines. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What a data service needs&lt;/th&gt;
&lt;th&gt;What &lt;code&gt;glassnode-go&lt;/code&gt; provides&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Familiar, discoverable access to standard metrics&lt;/td&gt;
&lt;td&gt;25 typed category services such as &lt;code&gt;Market&lt;/code&gt;, &lt;code&gt;Addresses&lt;/code&gt;, &lt;code&gt;Indicators&lt;/code&gt;, &lt;code&gt;Mining&lt;/code&gt;, and &lt;code&gt;Transactions&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Code is easier to navigate, review, and autocomplete. &lt;a href="https://github.com/tigusigalpa/glassnode-go/blob/main/docs/endpoint-coverage.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A way to reach newly introduced or unusual endpoints&lt;/td&gt;
&lt;td&gt;A generic &lt;code&gt;MetricsService&lt;/code&gt; for raw JSON, scalar time series, and object time series&lt;/td&gt;
&lt;td&gt;You do not have to wait for a typed wrapper to start experimenting. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clarity before making requests&lt;/td&gt;
&lt;td&gt;Runtime metadata methods for assets, metric paths, and supported parameters&lt;/td&gt;
&lt;td&gt;Fewer invalid requests and less guesswork around endpoint capabilities. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Resilience under load&lt;/td&gt;
&lt;td&gt;Automatic handling of HTTP 429 responses and configurable retry behavior&lt;/td&gt;
&lt;td&gt;Rate-limit logic stays centralized instead of leaking into every call site. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safer operations&lt;/td&gt;
&lt;td&gt;Header-based API-key authentication by default and redaction in URLs and error messages&lt;/td&gt;
&lt;td&gt;Secrets are less likely to appear in logs and diagnostics. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Start with a useful result, not a pile of boilerplate
&lt;/h2&gt;

&lt;p&gt;Installation is a familiar Go command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/tigusigalpa/glassnode-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The quick-start experience is intentionally small. Put your Glassnode API key in &lt;code&gt;GLASSNODE_API_KEY&lt;/code&gt;, create the client from the environment, and request the metric through a typed service. The following example retrieves BTC price data at daily resolution. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;

    &lt;span class="n"&gt;glassnode&lt;/span&gt; &lt;span class="s"&gt;"github.com/tigusigalpa/glassnode-go"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClientFromEnv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Market&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Price&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MetricQuery&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"BTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Resolution&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolution24h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"BTC price at %d: $%.2f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;point&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;V&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important detail is not merely that the example is short. It encodes a useful boundary: application code talks in terms of &lt;strong&gt;market data and queries&lt;/strong&gt;, while the SDK takes responsibility for building the request, applying authentication, decoding the response, and returning a typed time series. That separation makes a dashboard handler, scheduled research job, or backtest easier to test and evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Typed when you can; generic when you need to
&lt;/h2&gt;

&lt;p&gt;The primary API surface is made of category services that mirror Glassnode's documented endpoint families. For example, a dashboard can request OHLC data from &lt;code&gt;client.Market.PriceOHLC&lt;/code&gt;, activity data from &lt;code&gt;client.Addresses.ActiveCount&lt;/code&gt;, and an indicator such as SOPR from &lt;code&gt;client.Indicators.SOPR&lt;/code&gt;. The repository maintains an endpoint-coverage document that maps API paths to the corresponding SDK methods. &lt;a href="https://github.com/tigusigalpa/glassnode-go/blob/main/docs/endpoint-coverage.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But strongly typed wrappers should never become a release-cycle bottleneck. APIs evolve, new metrics appear, and some response shapes are inherently specialized. For those moments, &lt;code&gt;glassnode-go&lt;/code&gt; supplies the generic &lt;code&gt;MetricsService&lt;/code&gt;. It can return raw JSON through &lt;code&gt;Get&lt;/code&gt;, scalar time-series data through &lt;code&gt;GetTimePoints&lt;/code&gt;, or object time-series data through &lt;code&gt;GetObjectPoints&lt;/code&gt;. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Use a valid metric path even when you do not need a dedicated convenience method.&lt;/span&gt;
&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metrics&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GetTimePoints&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/indicators/sopr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MetricQuery&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;      &lt;span class="s"&gt;"BTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Resolution&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolution24h&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Received %d SOPR observations&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That combination is especially useful in real products. Your stable, frequently used calls can remain concise and self-documenting; exploratory work, internal analytics, and recently introduced paths can still proceed without compromising the client architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discover capabilities before you spend requests
&lt;/h2&gt;

&lt;p&gt;Metric paths alone do not tell the whole story. A query may support different resolutions, assets, bulk access, currencies, or time formats depending on the underlying endpoint. Guessing at those parameters often results in failed calls that are hard to diagnose in a busy service.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;glassnode-go&lt;/code&gt; exposes a metadata layer for exactly this reason. The &lt;code&gt;MetadataService&lt;/code&gt; can list supported assets, discover metric paths, and inspect a metric's available parameters at runtime. The SDK documentation recommends this metadata-first workflow before making data calls. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"/market/price_usd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Metric: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Supported resolutions: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Resolutions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a product team, this is more than a convenience. It lets a configuration screen, query builder, or data pipeline validate its choices against the API instead of embedding assumptions that eventually become stale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate limits deserve first-class engineering
&lt;/h2&gt;

&lt;p&gt;A high-quality SDK should make the correct behavior the default behavior. In &lt;code&gt;glassnode-go&lt;/code&gt;, HTTP 429 responses trigger automatic retries for idempotent GET requests. The client uses the &lt;code&gt;x-rate-limit-reset&lt;/code&gt; header when the server supplies it and falls back to exponential backoff when it does not. It avoids retrying 400, 401, and 404 responses because those represent problems a retry is unlikely to fix. Retry behavior can be configured with &lt;code&gt;WithRetry&lt;/code&gt;. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This decision is quietly powerful. Every endpoint call retains the same clean shape, while the behavior that protects the service is standardized in one place. When the retry budget is exhausted, the caller still gets actionable context through the exported &lt;code&gt;APIError&lt;/code&gt; type and the &lt;code&gt;ErrRateLimited&lt;/code&gt; sentinel. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Indicators&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SOPR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;MetricQuery&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Asset&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"BTC"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrRateLimited&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;glassnode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIError&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Rate limit exhausted; reset in %ds"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RateLimitReset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same philosophy applies to the rest of the error model. The package exposes sentinel errors for conditions such as bad requests, unauthorized access, missing metric paths, rate limits, and internal server errors. That gives callers the familiar &lt;code&gt;errors.Is&lt;/code&gt; and &lt;code&gt;errors.As&lt;/code&gt; workflow rather than forcing production code to branch on error-message strings. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Security should not be an afterthought
&lt;/h2&gt;

&lt;p&gt;An API key is an operational secret, not a configuration string that belongs in source code or a query URL. The library defaults to the &lt;code&gt;X-Api-Key&lt;/code&gt; header mode and supports query-string authentication only as an explicit opt-in for exceptional environments. It also documents API-key redaction in URLs, response metadata, and error messages. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The recommended developer experience is therefore straightforward: keep the key in &lt;code&gt;GLASSNODE_API_KEY&lt;/code&gt;, construct the client with &lt;code&gt;NewClientFromEnv()&lt;/code&gt;, and let the client carry the secret in headers. It is a small convention with a meaningful payoff in log hygiene and incident response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for dashboards today—and historical research tomorrow
&lt;/h2&gt;

&lt;p&gt;The SDK is not limited to a single-metric dashboard. Its &lt;code&gt;BulkQuery&lt;/code&gt; support can request data for explicitly specified multiple assets in one call, reducing round trips where the underlying metric supports bulk access. The project documentation also notes that the cost model remains per asset, so bulk requests optimize throughput rather than magically reducing credit consumption. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For researchers, the Point-in-Time API is another standout capability. PIT response models preserve a &lt;code&gt;computed_at&lt;/code&gt; timestamp alongside the observation timestamp. This matters when historical metric values may later be revised: a backtest can distinguish what the current dataset says from what was known at a particular historical moment. The SDK offers typed PIT response models and generic point-in-time methods for that workflow. &lt;a href="https://github.com/tigusigalpa/glassnode-go/blob/main/docs/endpoint-coverage.md" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use case&lt;/th&gt;
&lt;th&gt;A practical &lt;code&gt;glassnode-go&lt;/code&gt; path&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Market dashboard&lt;/td&gt;
&lt;td&gt;Typed &lt;code&gt;Market&lt;/code&gt;, &lt;code&gt;Indicators&lt;/code&gt;, and &lt;code&gt;Addresses&lt;/code&gt; services for the metrics displayed most often. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-asset research job&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Metrics.GetBulk&lt;/code&gt; with explicit asset lists, plus metadata checks before the request. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API explorer or no-code query builder&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Metadata.Assets&lt;/code&gt;, &lt;code&gt;Metadata.Metrics&lt;/code&gt;, and &lt;code&gt;Metadata.Metric&lt;/code&gt; to populate valid options dynamically. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strategy backtesting&lt;/td&gt;
&lt;td&gt;Point-in-Time response models that retain &lt;code&gt;computed_at&lt;/code&gt; for historically aware analysis. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Custom or newly added endpoint&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Metrics.Get&lt;/code&gt;, &lt;code&gt;GetTimePoints&lt;/code&gt;, or &lt;code&gt;GetObjectPoints&lt;/code&gt; with a valid metric path. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The developer experience is the feature
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;glassnode-go&lt;/code&gt; does not try to hide Glassnode behind a proprietary abstraction. Instead, it gives Go developers a reliable, conventional way to work with the Basic API: typed services for everyday endpoints, generic access when flexibility is needed, metadata for discovery, and production-minded defaults for context propagation, concurrency, security, and retries. The project's initial &lt;code&gt;v1.0.0&lt;/code&gt; release documents these capabilities along with typed response models, configurable transport options, and a mocked-transport test suite. &lt;a href="https://github.com/tigusigalpa/glassnode-go/blob/main/CHANGELOG.md" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your next Go project needs on-chain metrics, take the SDK for a spin, browse the examples, and consider contributing an issue, documentation improvement, or pull request. The repository is MIT licensed and welcomes contributions. &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/tigusigalpa/glassnode-go" rel="noopener noreferrer"&gt;github.com/tigusigalpa/glassnode-go&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post discusses developer tooling for accessing data. It is not investment, trading, or financial advice.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>go</category>
      <category>glassnode</category>
      <category>cryptocurrency</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>Stop Wrestling with cURL: Meet glassnode-php, an Unofficial Glassnode SDK for PHP</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Thu, 30 Jul 2026 16:41:15 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/stop-wrestling-with-curl-meet-glassnode-php-an-unofficial-glassnode-sdk-for-php-1999</link>
      <guid>https://dev.to/tigusigalpa/stop-wrestling-with-curl-meet-glassnode-php-an-unofficial-glassnode-sdk-for-php-1999</guid>
      <description>&lt;p&gt;Integrating on-chain metrics into a PHP product should not require every developer to become an HTTP error-handling specialist first. Yet that is often the reality: hand-written cURL calls, manually assembled query strings, JSON decoding scattered through services, and generic exceptions that make production incidents harder to diagnose. The problem becomes even more consequential when requests consume data credits and operate within provider-managed limits. Glassnode’s own documentation makes clear that API access, available data, and request consumption depend on the customer’s plan. &lt;a href="https://docs.glassnode.com/basic-api/api" rel="noopener noreferrer"&gt;2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;&lt;strong&gt;glassnode-php&lt;/strong&gt;&lt;/a&gt; is built to remove that friction. It is an &lt;strong&gt;unofficial&lt;/strong&gt;, production-oriented SDK for PHP 8.1+ that targets the Glassnode Basic API. The package is framework-agnostic, while also offering a first-class bridge for Laravel applications. It is not affiliated with or endorsed by Glassnode; instead, it is an independent developer tool designed to make integrations cleaner, safer, and much easier to maintain. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The short version:&lt;/strong&gt; &lt;code&gt;glassnode-php&lt;/code&gt; lets PHP developers spend less time wiring up HTTP and more time building dashboards, alerts, research tools, and data-driven product features.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why an SDK Matters Here
&lt;/h2&gt;

&lt;p&gt;A raw HTTP integration can be perfectly acceptable for a one-off script. It becomes much less attractive when an application needs many metrics, multiple assets, a predictable error model, secure key handling, and the ability to evolve as the upstream API evolves. That is the gap &lt;code&gt;glassnode-php&lt;/code&gt; addresses.&lt;/p&gt;

&lt;p&gt;Rather than exposing a single oversized client with an ever-growing list of methods, the SDK uses three complementary layers. At the foundation is &lt;code&gt;GlassnodeClient&lt;/code&gt;, which pairs configuration with a built-in cURL transport or a PSR-18-compatible HTTP client. Above that, the package provides 25 typed category resources covering the documented endpoint categories. Finally, metadata discovery and a generic &lt;code&gt;get()&lt;/code&gt; method give developers an escape hatch for valid metric paths that do not yet have a dedicated convenience wrapper. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Integration concern&lt;/th&gt;
&lt;th&gt;What &lt;code&gt;glassnode-php&lt;/code&gt; provides&lt;/th&gt;
&lt;th&gt;Practical result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Repetitive API plumbing&lt;/td&gt;
&lt;td&gt;Typed category resources and convenience methods&lt;/td&gt;
&lt;td&gt;Less manual URL and parameter handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evolving metric catalog&lt;/td&gt;
&lt;td&gt;Metadata discovery and generic metric access&lt;/td&gt;
&lt;td&gt;New valid paths can be used without waiting for a wrapper&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limiting&lt;/td&gt;
&lt;td&gt;Automatic handling of HTTP 429 responses&lt;/td&gt;
&lt;td&gt;More resilient background jobs and dashboards&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Credential exposure&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;X-Api-Key&lt;/code&gt; header authentication by default&lt;/td&gt;
&lt;td&gt;Fewer secrets embedded in URLs and logs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Laravel integration&lt;/td&gt;
&lt;td&gt;Auto-discovery, configuration publishing, Facade, and DI support&lt;/td&gt;
&lt;td&gt;A familiar experience in Laravel projects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The table above reflects package capabilities documented in the project README. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A Developer-Friendly API Surface
&lt;/h2&gt;

&lt;p&gt;The fastest way to appreciate the library is to look at its API shape. After installation, fetching Bitcoin price data does not require a request factory, an endpoint string, and a bespoke JSON decoder in your business logic. Instead, the code reads like the business question you are trying to answer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require tigusigalpa/glassnode-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Glassnode\GlassnodeClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Glassnode\GlassnodeConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GlassnodeConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'YOUR_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;15.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;retryAttempts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GlassnodeClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Fetch BTC price data with 24-hour resolution.&lt;/span&gt;
&lt;span class="nv"&gt;$prices&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'i'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'24h'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$prices&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"BTC price at %d: $%.2f&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'t'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;$point&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'v'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a small example, but the design scales well. The same client exposes resources for areas such as addresses, market data, indicators, mining, supply, transactions, derivatives, and more. The typed layer is useful when you already know the metric family you need, while the generic API makes the client more durable in the face of an evolving upstream platform. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// A generic call for any valid metric path.&lt;/span&gt;
&lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'/v1/metrics/addresses/sending_count'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Discover what an individual metric supports before requesting it.&lt;/span&gt;
&lt;span class="nv"&gt;$metric&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;metric&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/market/price_usd'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The metadata-first workflow deserves special attention. Glassnode documents metadata as a source of information about metric parameters, time ranges, descriptions, and assets. Using it at runtime gives a product team a more robust alternative to hardcoding assumptions about every endpoint. &lt;a href="https://docs.glassnode.com/basic-api/api" rel="noopener noreferrer"&gt;2&lt;/a&gt; In practice, it can help a dashboard expose only supported assets, allow users to select meaningful resolutions, or validate a metric choice before a more expensive data call is made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for the Unhappy Path, Too
&lt;/h2&gt;

&lt;p&gt;The happiest-path request is rarely what causes trouble in a production integration. Rate limits, bad credentials, invalid parameters, missing metrics, and temporary transport failures are the issues that tend to surface at the worst possible moment. &lt;code&gt;glassnode-php&lt;/code&gt; makes those cases first-class citizens.&lt;/p&gt;

&lt;p&gt;The package automatically retries HTTP &lt;code&gt;429 Too Many Requests&lt;/code&gt; responses for GET requests. When the server supplies an &lt;code&gt;x-rate-limit-reset&lt;/code&gt; header, the SDK uses it; otherwise, it falls back to exponential backoff. It deliberately does not retry client errors such as &lt;code&gt;400&lt;/code&gt;, &lt;code&gt;401&lt;/code&gt;, or &lt;code&gt;404&lt;/code&gt;, where retrying is unlikely to change the outcome. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt; That distinction saves developers from implementing the same defensive logic in every command, queue job, or controller.&lt;/p&gt;

&lt;p&gt;The exception model is equally practical. Instead of catching a generic failure and inspecting an opaque response, an application can handle &lt;code&gt;UnauthorizedException&lt;/code&gt;, &lt;code&gt;RateLimitException&lt;/code&gt;, &lt;code&gt;BadRequestException&lt;/code&gt;, and &lt;code&gt;NotFoundException&lt;/code&gt; separately. This makes it much easier to turn an API event into the correct product behaviour: surface a configuration problem, reschedule a job, prompt for a valid asset, or report that a requested metric does not exist. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Glassnode\Exceptions\RateLimitException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Glassnode\Exceptions\UnauthorizedException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Glassnode\Exceptions\GlassnodeException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$sopr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;indicators&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sopr&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UnauthorizedException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Alert the operator to fix the API-key configuration.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RateLimitException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Requeue or delay the job using the available reset context.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GlassnodeException&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Handle transport or other SDK-level failures.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Security has also been treated as a default, not an optional enhancement. The SDK sends credentials through the &lt;code&gt;X-Api-Key&lt;/code&gt; header by default, although a query-parameter mode is available where it is required. Glassnode’s API documentation supports both forms of authentication. &lt;a href="https://docs.glassnode.com/basic-api/api" rel="noopener noreferrer"&gt;1&lt;/a&gt; Keeping a key out of URLs is a sensible default for applications that may record URLs in logs, monitoring systems, proxies, or analytics tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Natural Fit for Laravel
&lt;/h2&gt;

&lt;p&gt;Laravel developers do not need to sacrifice framework conventions to use a specialized API client. &lt;code&gt;glassnode-php&lt;/code&gt; supports service-provider and Facade auto-discovery, configuration publishing, and dependency injection. That means a Laravel project can keep secrets in &lt;code&gt;.env&lt;/code&gt;, organize request logic inside services, and inject &lt;code&gt;GlassnodeClient&lt;/code&gt; where it is needed. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Glassnode\Laravel\Facades\Glassnode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Glassnode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;market&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'i'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'24h'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$sopr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Glassnode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;indicators&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sopr&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="nv"&gt;$assets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Glassnode&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For larger applications, dependency injection provides an especially testable option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\View\View&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\Glassnode\GlassnodeClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DashboardController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;GlassnodeClient&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;View&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dashboard'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="s1"&gt;'price'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;market&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;price&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
            &lt;span class="s1"&gt;'activeAddresses'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;addresses&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;activeCount&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'a'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach keeps the controller focused on application orchestration rather than request construction. The same client can be reused in a scheduled command, a queue worker, or a reporting service without duplicating API configuration or error-handling code.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Than a Convenience Wrapper
&lt;/h2&gt;

&lt;p&gt;The value of &lt;code&gt;glassnode-php&lt;/code&gt; is not merely that it shortens syntax. Its transport flexibility, immutable configuration, metadata discovery, bulk-query support, rate-limit awareness, and detailed exceptions form a foundation for integrations that need to survive real operational conditions. The package also supports point-in-time metrics, preserving the &lt;code&gt;computed_at&lt;/code&gt; timestamp for workflows that need to distinguish between a value as known at a specific time and later-updated data. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bulk requests are another useful capability when a product needs to retrieve a metric for several explicitly selected assets. They can reduce HTTP round trips, but developers should still model request volume and data usage according to Glassnode’s service rules and plan entitlements. &lt;a href="https://docs.glassnode.com/basic-api/api" rel="noopener noreferrer"&gt;1&lt;/a&gt; The SDK is designed to make calls correct and manageable; access scope, data availability, and credits remain governed by Glassnode.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Building with Less Plumbing
&lt;/h2&gt;

&lt;p&gt;If you are building a crypto analytics dashboard, an internal reporting system, a market-monitoring service, or any PHP application that needs on-chain metrics, &lt;code&gt;glassnode-php&lt;/code&gt; offers a clear path away from fragile ad hoc requests. Install it with Composer, configure your Glassnode API key through environment variables, begin with the metadata endpoints, and let the SDK deal with the repetitive integration work.&lt;/p&gt;

&lt;p&gt;Explore the repository, review the examples, and contribute improvements at &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;&lt;strong&gt;github.com/tigusigalpa/glassnode-php&lt;/strong&gt;&lt;/a&gt;. The project is released under the MIT License. &lt;a href="https://github.com/tigusigalpa/glassnode-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This SDK is an engineering integration tool, not a source of investment advice. Any analysis based on retrieved data should be evaluated within the context of your own methodology and applicable requirements.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>glassnode</category>
      <category>trading</category>
    </item>
    <item>
      <title>Stop Fighting the CryptoPanic API: Meet cryptopanic-php, the Modern PHP SDK</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Wed, 29 Jul 2026 14:42:43 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/stop-fighting-the-cryptopanic-api-meet-cryptopanic-php-the-modern-php-sdk-1il2</link>
      <guid>https://dev.to/tigusigalpa/stop-fighting-the-cryptopanic-api-meet-cryptopanic-php-the-modern-php-sdk-1il2</guid>
      <description>&lt;p&gt;Integrating a news aggregator API into your application often sounds straightforward. You just need to pull a few endpoints, parse some JSON, and display the results. However, when working with financial data platforms like CryptoPanic, the reality quickly becomes complicated. You find yourself writing boilerplate code for authentication, handling evolving response schemas, implementing retry logic for rate limits, and managing pagination state. Every project seems to demand the same tedious plumbing.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;CryptoPanic API&lt;/a&gt; is an incredibly powerful resource for developers building crypto trackers, trading bots, and portfolio dashboards. Yet, consuming it cleanly in PHP has historically required developers to reinvent the wheel. This is where &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;cryptopanic-php&lt;/a&gt; steps in. It is a modern, framework-neutral PHP SDK designed to make fetching crypto news, managing portfolios, and consuming RSS feeds an elegant experience.&lt;/p&gt;

&lt;p&gt;In this article, we will explore why you should stop fighting raw cURL requests and start using &lt;code&gt;cryptopanic-php&lt;/code&gt; to build robust crypto applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Another SDK?
&lt;/h2&gt;

&lt;p&gt;When integrating external services, the goal is to spend time building business logic, not wrestling with HTTP clients. &lt;code&gt;cryptopanic-php&lt;/code&gt; was built with modern PHP practices in mind, targeting PHP 8.1 and above. It provides a typed, predictable, and safe interface to the CryptoPanic API &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Typed Response Models
&lt;/h3&gt;

&lt;p&gt;Instead of dealing with anonymous arrays returned by &lt;code&gt;json_decode()&lt;/code&gt;, the SDK maps known response shapes into strictly typed objects. When you request a page of posts, you receive a &lt;code&gt;PostsPage&lt;/code&gt; object containing an array of &lt;code&gt;Post&lt;/code&gt; instances. Each &lt;code&gt;Post&lt;/code&gt; exposes properties like &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;source&lt;/code&gt;, &lt;code&gt;votes&lt;/code&gt;, and publication dates parsed into &lt;code&gt;DateTimeImmutable&lt;/code&gt; objects. This allows your IDE to provide accurate autocompletion and static analysis tools like PHPStan to catch errors before they reach production &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safe and Immutable Design
&lt;/h3&gt;

&lt;p&gt;Security and predictability are paramount when handling API tokens. The SDK ensures that your authentication token is never included in exception messages or data dumps. Furthermore, when the API returns pagination URLs, the SDK automatically redacts the token from them &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The core components, such as &lt;code&gt;CryptoPanicConfig&lt;/code&gt; and &lt;code&gt;PostsQuery&lt;/code&gt;, are designed as immutable value objects. When you need to request the next page or modify a filter, you use the &lt;code&gt;with()&lt;/code&gt; method to create a new instance without mutating the original state. This functional approach eliminates side effects and makes your code easier to reason about &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Robust Error Handling and Retries
&lt;/h3&gt;

&lt;p&gt;Network requests fail, and APIs enforce rate limits. &lt;code&gt;cryptopanic-php&lt;/code&gt; abstracts these challenges with a rich exception hierarchy. Instead of catching generic HTTP errors, you can handle specific scenarios:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Situation&lt;/th&gt;
&lt;th&gt;Dedicated exception&lt;/th&gt;
&lt;th&gt;Practical response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Invalid or missing token&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UnauthorizedException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Refresh or replace the credential without exposing it in application logs.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plan-restricted endpoint&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ForbiddenException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Adjust the workflow or verify account access before retrying.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request quota exceeded&lt;/td&gt;
&lt;td&gt;&lt;code&gt;RateLimitException&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Respect the available &lt;code&gt;retryAfter&lt;/code&gt; guidance and slow down.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These explicit failure modes make it easier to implement intentional recovery paths rather than treating every unsuccessful request as the same generic error &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Moreover, the SDK includes configurable retry logic with exponential backoff. You can instruct the client to automatically retry transient failures like HTTP 429 (Too Many Requests) or 502 (Bad Gateway) without writing a custom retry loop &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Installing the package is as simple as running a Composer command. The SDK requires PHP 8.1+, &lt;code&gt;ext-json&lt;/code&gt;, and &lt;code&gt;ext-curl&lt;/code&gt; &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require tigusigalpa/cryptopanic-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Framework-Neutral Usage
&lt;/h3&gt;

&lt;p&gt;If you are building a plain PHP worker or a Symfony service, you can initialize the client using environment variables. Keep your token secure in your environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CRYPTOPANIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-super-secret-token"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, instantiate the client and fetch a page of posts filtered by specific currencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="k"&gt;declare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict_types&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\CryptoPanicClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\PostsQuery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\Enums\Filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CryptoPanicClient&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromEnv&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostsQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;currencies&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'BTC'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ETH'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Rising&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"[%s] %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'unknown'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s1"&gt;'Untitled'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publishedAt&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Published: '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publishedAt&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;DATE_ATOM&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="kc"&gt;PHP_EOL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;PostsQuery&lt;/code&gt; object validates your parameters locally, ensuring you do not send malformed requests. It also uses backed enums for known filters and kinds, while remaining flexible enough to accept custom strings for forward compatibility &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  First-Class Laravel Integration
&lt;/h2&gt;

&lt;p&gt;While the SDK is framework-neutral, it shines particularly bright in Laravel applications. The package includes an auto-discovered service provider that registers the client as a singleton, merges configuration, and provides a convenient Facade &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After installation, you can publish the configuration file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan vendor:publish &lt;span class="nt"&gt;--tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cryptopanic-config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your credentials to your &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;CRYPTOPANIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;your-token&lt;/span&gt;
&lt;span class="py"&gt;CRYPTOPANIC_API_PLAN&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;growth&lt;/span&gt;
&lt;span class="py"&gt;CRYPTOPANIC_TIMEOUT&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;15&lt;/span&gt;
&lt;span class="py"&gt;CRYPTOPANIC_RETRY_ATTEMPTS&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;3&lt;/span&gt;
&lt;span class="py"&gt;CRYPTOPANIC_RETRY_DELAY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now inject the &lt;code&gt;CryptoPanicClient&lt;/code&gt; directly into your controllers or use the Facade for rapid development:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Http\Controllers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\JsonResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\Enums\Filter&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\Laravel\Facades\CryptoPanic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\PostsQuery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CryptoNewsController&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;JsonResponse&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CryptoPanic&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PostsQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;currencies&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SOL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'ADA'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Important&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;));&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service provider defers network connections until you actually make a request, ensuring it does not impact your application's boot time &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Capabilities
&lt;/h2&gt;

&lt;p&gt;The SDK is not limited to just fetching JSON posts. It provides comprehensive coverage of the CryptoPanic API surface.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Unstable Schemas
&lt;/h3&gt;

&lt;p&gt;Some API endpoints, like the Portfolio endpoint, do not have a strictly defined public schema. Instead of guessing domain fields and risking breakages when the API evolves, the SDK returns a &lt;code&gt;PortfolioResponse&lt;/code&gt; where the &lt;code&gt;raw&lt;/code&gt; property contains the decoded JSON. This treats the unstable endpoint as an integration boundary, allowing you to map the data into your own DTOs &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$portfolio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;portfolio&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// Inspect the raw array and map it to your application's needs&lt;/span&gt;
&lt;span class="nv"&gt;$data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$portfolio&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Raw RSS Access
&lt;/h3&gt;

&lt;p&gt;If your application relies on XML feeds, the SDK provides dedicated methods for the RSS endpoints. Methods like &lt;code&gt;postsRss()&lt;/code&gt; and &lt;code&gt;newsRss()&lt;/code&gt; return an &lt;code&gt;RSSResponse&lt;/code&gt; containing the raw XML body, allowing you to stream it directly or parse it with your preferred XML library &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$feed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;newsRss&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Content-Type: application/rss+xml'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$feed&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Flexible Transports
&lt;/h3&gt;

&lt;p&gt;By default, the SDK uses a lightweight cURL transport to minimize dependencies. However, if your application already standardizes on PSR-18 HTTP clients (like Guzzle), you can inject your preferred client and request factory &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;GuzzleHttp\Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;GuzzleHttp\Psr7\HttpFactory&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\CryptoPanicClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\CryptoPanic\CryptoPanicConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CryptoPanicClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;CryptoPanicConfig&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromEnv&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;psrClient&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;requestFactory&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;HttpFactory&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This flexibility ensures that &lt;code&gt;cryptopanic-php&lt;/code&gt; plays nicely with your existing logging, middleware, and mocking strategies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Navigating API Plans
&lt;/h2&gt;

&lt;p&gt;It is important to note that the CryptoPanic API operates on a tiered plan system. Its public documentation labels the free Developer API plan as discontinued, so you should verify the current commercial details in your CryptoPanic account rather than assume free access is available &lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;api&lt;/a&gt;. Feature availability is determined by CryptoPanic, not by the SDK.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API capability&lt;/th&gt;
&lt;th&gt;Current plan note in the public API reference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;portfolio()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Listed for Growth and Enterprise plans.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;size&lt;/code&gt;, &lt;code&gt;with_content&lt;/code&gt;, and &lt;code&gt;search&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Listed as Enterprise-gated parameters.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSS responses&lt;/td&gt;
&lt;td&gt;Documented as returning 20 items regardless of API plan.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These constraints come from the API provider and may change; the package surfaces the response cleanly while preserving CryptoPanic as the authority for account entitlements &lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The SDK validates obvious syntax locally but defers to CryptoPanic for account entitlements. If you attempt to use a feature outside your plan, the SDK will gracefully throw a &lt;code&gt;ForbiddenException&lt;/code&gt;, allowing your application to handle the restriction cleanly &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Building applications around financial news requires reliability and precision. &lt;code&gt;cryptopanic-php&lt;/code&gt; delivers both by abstracting the complexities of the CryptoPanic API behind a modern, strictly typed, and developer-friendly interface. Whether you are orchestrating background workers in plain PHP or building a rapid prototype in Laravel, this SDK provides the tools you need to succeed.&lt;/p&gt;

&lt;p&gt;Stop writing boilerplate API wrappers and start focusing on your application's unique value. Check out the &lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;cryptopanic-php repository on GitHub&lt;/a&gt;, explore the source code, and give it a star if it saves you time on your next crypto project.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/tigusigalpa/cryptopanic-php" rel="noopener noreferrer"&gt;repo&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;api&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>cryptocurrency</category>
      <category>cryptopanic</category>
    </item>
    <item>
      <title>Stop Plumbing Crypto News APIs by Hand: Introducing cryptopanic-go</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Wed, 29 Jul 2026 09:16:08 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/stop-plumbing-crypto-news-apis-by-hand-introducing-cryptopanic-go-j08</link>
      <guid>https://dev.to/tigusigalpa/stop-plumbing-crypto-news-apis-by-hand-introducing-cryptopanic-go-j08</guid>
      <description>&lt;p&gt;Building a crypto-facing product in Go can begin with a deceptively small task: fetch a news feed, filter it for a few assets, and surface the result in a dashboard, worker, or command-line tool. The integration becomes much less small once the production concerns arrive. You need a versioned API path, authenticated requests, query encoding, typed decoding, deadlines, pagination, useful failures, and a safe policy for transient outages.&lt;/p&gt;

&lt;p&gt;The upstream CryptoPanic API exposes a posts endpoint with filters for currencies, regions, content kind, and pagination, and it requires an authentication token for requests.&lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;2&lt;/a&gt; That is an entirely reasonable HTTP API. The question for a Go team is whether each application should rebuild the same integration layer around it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cryptopanic-go&lt;/code&gt; is a focused answer to that question: a production-oriented Go SDK for the CryptoPanic API. Rather than wrapping the API in a framework or inventing business abstractions, it supplies a small, typed, context-aware client that is appropriate for services, background workers, and CLI tools.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“The SDK deliberately stays close to the public CryptoPanic API. It does not invent a portfolio schema, hide server-side plan rules, or force a framework on your application.” — &lt;code&gt;cryptopanic-go&lt;/code&gt; README &lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article shows what that design buys you, how to get started, and where the SDK fits in a production Go codebase. It discusses integration engineering, not trading strategy or financial advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem is not making one HTTP request
&lt;/h2&gt;

&lt;p&gt;A raw &lt;code&gt;net/http&lt;/code&gt; request is often the correct first prototype. But the moment a crypto-news feature becomes part of a real product, the surrounding code tends to spread across handlers, workers, and scripts. Each call site may have to reconstruct the API URL, serialize filters, decode a response, decide what to retry, and avoid logging secrets. Those are all solvable problems—but they are poor places to spend the same engineering effort repeatedly.&lt;/p&gt;

&lt;p&gt;The API itself returns &lt;code&gt;next&lt;/code&gt;, &lt;code&gt;previous&lt;/code&gt;, and &lt;code&gt;results&lt;/code&gt; in a posts response.&lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;2&lt;/a&gt; A generic client leaves your application responsible for interpreting those values, including making sure an authentication token in an upstream URL never makes it into a log line. Likewise, upstream permissions and plan-gated parameters remain important even when your request code is concise. A useful SDK should reduce boilerplate without concealing those operational realities.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Integration concern&lt;/th&gt;
&lt;th&gt;What repetitive hand-written code must handle&lt;/th&gt;
&lt;th&gt;What &lt;code&gt;cryptopanic-go&lt;/code&gt; provides&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request construction&lt;/td&gt;
&lt;td&gt;API plan paths, query encoding, and optional filters&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;PostsQuery&lt;/code&gt;, typed constants, and configurable client options.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Request lifetime&lt;/td&gt;
&lt;td&gt;Cancellation and an operation-specific deadline&lt;/td&gt;
&lt;td&gt;A &lt;code&gt;context.Context&lt;/code&gt; on every network-facing method.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sensitive data&lt;/td&gt;
&lt;td&gt;Avoiding token leakage through URLs and errors&lt;/td&gt;
&lt;td&gt;Token redaction in pagination URLs and SDK errors.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Transient failure&lt;/td&gt;
&lt;td&gt;Backoff, retry selection, and &lt;code&gt;Retry-After&lt;/code&gt; handling&lt;/td&gt;
&lt;td&gt;An opt-in retry policy with capped exponential backoff and jitter.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Response shape&lt;/td&gt;
&lt;td&gt;JSON structs, optional fields, and raw endpoint formats&lt;/td&gt;
&lt;td&gt;Typed post models, deliberate raw portfolio JSON, and raw RSS XML.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The result is not “magic.” It is simply a clearer boundary: application code decides &lt;strong&gt;which&lt;/strong&gt; data matters, while the client consistently handles the mechanics of asking for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why &lt;code&gt;cryptopanic-go&lt;/code&gt; is a good fit for Go services
&lt;/h2&gt;

&lt;p&gt;The library is intentionally compact. It requires Go 1.22 or newer and uses only the Go standard library at runtime, so adopting it does not introduce a broad transitive dependency tree.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; A configured &lt;code&gt;Client&lt;/code&gt; can also be shared safely between goroutines, which matches the normal shape of Go servers and workers.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More importantly, it uses types to make the common path explicit. Instead of decoding unstructured maps, you receive models such as &lt;code&gt;Post&lt;/code&gt; and &lt;code&gt;PostsPage&lt;/code&gt;. Optional or plan-gated values are represented carefully, so your code has a natural reason to check whether data is actually present before relying on it.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; That is a better failure mode than assuming a field will always exist and discovering otherwise at runtime.&lt;/p&gt;

&lt;p&gt;The library also chooses not to over-model the upstream API where the source contract is not stable enough. For example, &lt;code&gt;Portfolio&lt;/code&gt; returns a &lt;code&gt;PortfolioResponse&lt;/code&gt; containing &lt;code&gt;json.RawMessage&lt;/code&gt;; the README explains that this avoids guessing at a complete portfolio schema that the public API does not define as stable.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; The RSS methods similarly return raw XML rather than pretending it is the same thing as a JSON news page.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; Those choices preserve flexibility for the application instead of locking it into a potentially inaccurate SDK abstraction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install it and make the first request
&lt;/h2&gt;

&lt;p&gt;Install the package with Go's standard tooling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/tigusigalpa/cryptopanic-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repository documents &lt;code&gt;CRYPTOPANIC_AUTH_TOKEN&lt;/code&gt; as the environment variable used by &lt;code&gt;NewFromEnv&lt;/code&gt;; keeping the token outside source control is the recommended starting point.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CRYPTOPANIC_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-token"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is a compact, production-shaped example. It creates one reusable client, configures a client timeout and retry policy, then applies a shorter deadline to this individual operation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"context"&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;

    &lt;span class="n"&gt;cryptopanic&lt;/span&gt; &lt;span class="s"&gt;"github.com/tigusigalpa/cryptopanic-go"&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewFromEnv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithPlan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PlanGrowth&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithRetryPolicy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RetryPolicy&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;MaxAttempts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Background&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;cancel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostsQuery&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Currencies&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"BTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ETH"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FilterRising&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntPtr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Results&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[%s] %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Kind&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example follows the package's documented configuration model: choose the API plan explicitly, set the underlying client timeout deliberately, and give the request its own context deadline.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; The explicit plan matters because access and available parameters are ultimately determined by the upstream CryptoPanic account and plan rather than by the SDK itself.&lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Filter without manually assembling query strings
&lt;/h2&gt;

&lt;p&gt;The core &lt;code&gt;Posts&lt;/code&gt; method accepts a &lt;code&gt;PostsQuery&lt;/code&gt;. Empty fields are omitted from the request, while the client validates rules that are safe to check locally; the upstream service remains the authority on evolving account permissions and capabilities.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That distinction is useful in real applications. You can write an intent-oriented query in Go and still keep the underlying API semantics visible in your code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
&lt;span class="n"&gt;pageNumber&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostsQuery&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Currencies&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"BTC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ETH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"SOL"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;Regions&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;    &lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Kind&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;KindNews&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Filter&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FilterHot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Size&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;       &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Results&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"source:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Source&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK exposes typed constants for commonly documented values such as filters, kinds, panic periods, panic sorting, and plans.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; The underlying API documents filters including currencies, regions, &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;kind&lt;/code&gt;, and &lt;code&gt;page&lt;/code&gt; for its posts endpoint.&lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;2&lt;/a&gt; That combination gives you good editor support without making the client falsely claim control over server-side entitlement rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pagination that does not spread secret URLs through your code
&lt;/h2&gt;

&lt;p&gt;Pagination is exactly the type of small detail that gets copied badly. CryptoPanic returns next and previous URLs in its response envelope.&lt;a href="https://cryptopanic.com/developers/api/" rel="noopener noreferrer"&gt;2&lt;/a&gt; &lt;code&gt;cryptopanic-go&lt;/code&gt; preserves the useful paging information while redacting the authentication token and extracting page numbers where possible. Its &lt;code&gt;PostsPage&lt;/code&gt; exposes helpers such as &lt;code&gt;HasNext&lt;/code&gt;, &lt;code&gt;HasPrevious&lt;/code&gt;, &lt;code&gt;NextPage&lt;/code&gt;, and &lt;code&gt;PreviousPage&lt;/code&gt;.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That makes a page-by-page retrieval loop direct and easy to review:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostsQuery&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IntPtr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasNext&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NextPage&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PostsQuery&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NextPage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;post&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Results&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;next&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The important design choice is that your application keeps sending a normal &lt;code&gt;PostsQuery&lt;/code&gt;; it is not asked to treat an upstream pagination URL as an executable instruction. That is both easier to test and safer to log.&lt;/p&gt;

&lt;h2&gt;
  
  
  Treat errors as part of the interface
&lt;/h2&gt;

&lt;p&gt;In production, a useful client does more than return a non-nil error. It tells your application whether the error is a configuration problem, an access problem, a rate-limit event, or a server-side failure.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cryptopanic-go&lt;/code&gt; provides sentinel errors that work with &lt;code&gt;errors.Is&lt;/code&gt;, including &lt;code&gt;ErrMissingAuthToken&lt;/code&gt;, &lt;code&gt;ErrInvalidQuery&lt;/code&gt;, &lt;code&gt;ErrUnauthorized&lt;/code&gt;, &lt;code&gt;ErrForbidden&lt;/code&gt;, &lt;code&gt;ErrRateLimited&lt;/code&gt;, and &lt;code&gt;ErrServerError&lt;/code&gt;. It also exposes an &lt;code&gt;APIError&lt;/code&gt; that can be inspected with &lt;code&gt;errors.As&lt;/code&gt; for details such as the status code, request ID, and retry count.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Posts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrUnauthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authentication failed; check the configured token."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrRateLimited&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The rate limit was reached and retries were exhausted."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrServerError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"The upstream service returned a server error."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;apiErr&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;cryptopanic&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;APIError&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;As&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status=%d request_id=%s retried=%d&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;RequestID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;apiErr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Retried&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This style lets a worker send an operational signal for an invalid token, apply its own queuing policy after a rate limit, or preserve a request ID for troubleshooting. The SDK documents that its error details use a redacted, truncated raw body, which is a thoughtful safeguard when diagnostics are routed to centralized logging.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Retry deliberately, not by default
&lt;/h2&gt;

&lt;p&gt;Automatic retries are useful only when they are intentional. The library leaves retries disabled by default and makes them an explicit &lt;code&gt;RetryPolicy&lt;/code&gt; choice.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; When enabled, its documented retry scope covers HTTP 429, 500, 502, and 503, plus selected transient network failures; permanent failures such as bad requests, unauthorized access, invalid URLs, and certificate failures are not retried.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The policy uses exponential backoff with jitter, caps the delay, and respects an upstream &lt;code&gt;Retry-After&lt;/code&gt; header within that cap.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; That is a sensible foundation for idempotent &lt;code&gt;GET&lt;/code&gt; operations such as reading news. It does not remove the need for application-level discipline: use a context deadline, apply caching where your traffic pattern calls for it, and make sure a retry policy aligns with the workload you operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  More than one endpoint, without pretending they are the same
&lt;/h2&gt;

&lt;p&gt;The package currently provides a small surface area that maps to meaningful API operations.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API task&lt;/th&gt;
&lt;th&gt;Go method&lt;/th&gt;
&lt;th&gt;Returned form&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fetch filtered posts&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.Posts(ctx, query)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;*PostsPage&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Typed news data and paging helpers support normal application flows.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read a portfolio response&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.Portfolio(ctx)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*PortfolioResponse&lt;/code&gt; with raw JSON&lt;/td&gt;
&lt;td&gt;Your application can decode an evolving schema on its own terms.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read filtered posts as RSS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.PostsRSS(ctx, query)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*RSSResponse&lt;/code&gt; with raw XML&lt;/td&gt;
&lt;td&gt;RSS stays XML instead of being forced into JSON models.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Read the news RSS feed&lt;/td&gt;
&lt;td&gt;&lt;code&gt;client.NewsRSS(ctx)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;*RSSResponse&lt;/code&gt; with raw XML&lt;/td&gt;
&lt;td&gt;A simple option for feed-oriented integrations.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is a strong example of an SDK being useful without becoming large. It handles the repeated transport work, preserves the distinctions between the remote formats, and lets your application own its own product model.&lt;/p&gt;

&lt;h2&gt;
  
  
  Production-minded, testable, and easy to adopt
&lt;/h2&gt;

&lt;p&gt;The project supports custom &lt;code&gt;*http.Client&lt;/code&gt; values for proxies, transports, TLS configuration, and testing. Its &lt;code&gt;WithBaseURL&lt;/code&gt; option is designed for mock servers, while &lt;code&gt;WithSleeper&lt;/code&gt; supports deterministic retry tests.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt; The repository's own tests use &lt;code&gt;httptest.Server&lt;/code&gt; and fixtures rather than live API calls, a practical pattern worth retaining in your own test suite.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For applications that need CryptoPanic data but do not want a broad framework or a pile of copied HTTP boilerplate, &lt;code&gt;cryptopanic-go&lt;/code&gt; makes a compelling trade-off. You get a small Go-native interface, explicit configuration, typed data where the API schema is stable, raw data where it is not, and operational safeguards around timeouts, retries, and credentials.&lt;/p&gt;

&lt;p&gt;If that matches your use case, start with the repository, run the examples, and adapt the query and error-handling patterns to your service. You can find the source, documentation, and contribution guidance on GitHub.&lt;a href="https://github.com/tigusigalpa/cryptopanic-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Try it:&lt;/strong&gt; &lt;code&gt;go get github.com/tigusigalpa/cryptopanic-go&lt;/code&gt; and build the integration code your product actually needs—not another layer of hand-rolled request plumbing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

</description>
      <category>go</category>
      <category>cryptopanic</category>
      <category>bitcoin</category>
      <category>trading</category>
    </item>
    <item>
      <title>Building Reliable Crypto Alerting Systems in Go: A Deep Dive into the whale-alert-go SDK</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Sat, 25 Jul 2026 07:39:30 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/building-reliable-crypto-alerting-systems-in-go-a-deep-dive-into-the-whale-alert-go-sdk-454</link>
      <guid>https://dev.to/tigusigalpa/building-reliable-crypto-alerting-systems-in-go-a-deep-dive-into-the-whale-alert-go-sdk-454</guid>
      <description>&lt;p&gt;Tracking large cryptocurrency transactions—commonly known as "whales"—has become a crucial tool for traders, analysts, and blockchain researchers. The Whale Alert Enterprise API is one of the leading data providers in this space, offering both historical data and real-time alerts. However, integrating third-party APIs into Go applications often requires writing a significant amount of boilerplate code. Developers must manually implement robust HTTP clients, manage the lifecycle of WebSocket connections, carefully handle pagination, and resolve floating-point precision issues when dealing with financial data.&lt;/p&gt;

&lt;p&gt;To address these challenges, the &lt;code&gt;whale-alert-go&lt;/code&gt; SDK was created &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;. This unofficial Go library is designed specifically for interacting with the Whale Alert Enterprise API. It abstracts away the routine networking tasks, allowing engineers to focus entirely on the business logic of their applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Philosophy: Idiomatic Go
&lt;/h2&gt;

&lt;p&gt;When designing &lt;code&gt;whale-alert-go&lt;/code&gt;, the primary focus was on adhering to the standards and best practices of the Go programming language. Every method that performs a network request accepts a &lt;code&gt;context.Context&lt;/code&gt; &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;. This ensures proper timeout management and the ability to cancel long-running operations, which is critical for high-load microservices. Furthermore, the client is designed to be entirely concurrency-safe, meaning it can be shared across multiple goroutines without the need for additional mutexes &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;One of the most common pitfalls when working with cryptocurrency APIs is using the &lt;code&gt;float64&lt;/code&gt; type to represent monetary amounts. Due to the nature of floating-point arithmetic, this inevitably leads to a loss of precision when dealing with very small values (such as satoshi fees) or extremely large numbers. In &lt;code&gt;whale-alert-go&lt;/code&gt;, this problem is solved radically: all monetary values and fees are preserved as strings &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;. Developers can pass these strings directly into specialized decimal arithmetic packages, such as &lt;code&gt;shopspring/decimal&lt;/code&gt;, completely eliminating the risk of data distortion.&lt;/p&gt;

&lt;h2&gt;
  
  
  REST API: Clean and Resilient
&lt;/h2&gt;

&lt;p&gt;Working with REST APIs often involves network failures, rate limits, and temporary server errors. The SDK provides a powerful retry mechanism that can be configured during client initialization using functional options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Create a client with retries enabled: up to 3 attempts, starting at&lt;/span&gt;
&lt;span class="c"&gt;// 500ms and capped at 10s. Retries only apply to idempotent GET requests.&lt;/span&gt;
&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;whalealert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;whalealert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WithRetry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The retry policy is implemented with safety as the top priority. Only idempotent GET requests are retried, eliminating the risk of duplicating transactions or unintentionally altering state &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;. The client automatically responds to HTTP status 429 (Too Many Requests) and 5xx series errors. It uses an exponential backoff strategy with added jitter (random deviation) to prevent the "thundering herd" effect, and it also respects the &lt;code&gt;Retry-After&lt;/code&gt; header if provided by the server &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  WebSocket Alerts: Built to Survive
&lt;/h2&gt;

&lt;p&gt;For real-time data delivery, Whale Alert provides a WebSocket API. Maintaining long-lived connections is a complex task because networks are inherently unstable and servers may forcefully drop sessions. The &lt;code&gt;websocket&lt;/code&gt; package included in the SDK offers an elegant solution with automatic reconnection capabilities &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;wsURL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Reconnect&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReconnectConfig&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;MaxAttempts&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;  &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;InitialDelay&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;MaxDelay&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;     &lt;span class="m"&gt;30&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Second&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;OnMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;msg&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventType&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;websocket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;EventTypeAlert&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Alert&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[ALERT] %s: %s&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Blockchain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Alert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A developer simply needs to define the reconnection parameters and register handlers for messages and errors. The client independently manages the read loop, decodes incoming events into strongly typed structs, and handles ping/pong control messages to keep the connection alive &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safe Pagination and Error Handling
&lt;/h2&gt;

&lt;p&gt;Endpoints that return lists, such as transaction histories, require pagination. &lt;code&gt;whale-alert-go&lt;/code&gt; offers two approaches. The first is a lazy &lt;code&gt;TransactionIterator&lt;/code&gt; that hides the logic of fetching subsequent pages under the hood &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;. The second approach allows developers to manually manage navigation using the &lt;code&gt;Next URL&lt;/code&gt; links.&lt;/p&gt;

&lt;p&gt;A notable security feature of the SDK is its validation of URLs for subsequent pages. The library verifies that the &lt;code&gt;Next URL&lt;/code&gt; shares the same origin as the client's base URL, preventing potential redirection attacks to malicious servers &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Error handling is also executed in the best traditions of Go 1.13+. The API returns a typed &lt;code&gt;APIError&lt;/code&gt; containing the HTTP status and the message from the server. In addition, sentinel errors such as &lt;code&gt;ErrUnauthorized&lt;/code&gt; or &lt;code&gt;ErrRateLimited&lt;/code&gt; are provided, which can be easily checked using &lt;code&gt;errors.Is&lt;/code&gt; and &lt;code&gt;errors.As&lt;/code&gt; &lt;a href="https://github.com/tigusigalpa/whale-alert-go" rel="noopener noreferrer"&gt;1&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Integrating with financial and cryptocurrency APIs demands a high degree of reliability. The &lt;code&gt;whale-alert-go&lt;/code&gt; library provides developers with a ready-to-use, carefully designed toolkit that resolves issues related to network instability, data precision loss, and connection management. Thanks to strongly typed models and an idiomatic approach, writing code becomes faster and safer.&lt;/p&gt;

&lt;p&gt;You can install the library using the standard Go command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go get github.com/tigusigalpa/whale-alert-go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Please note that this is an unofficial SDK, and you will need a valid Whale Alert Enterprise API key to access authenticated endpoints. Check out the source code, examples, and full documentation in the project's GitHub repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

</description>
      <category>go</category>
      <category>whalealert</category>
      <category>cryptocurrency</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>Track Blockchain Whale Activity in PHP with whale-alert-php</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Thu, 23 Jul 2026 06:10:20 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/track-blockchain-whale-activity-in-php-with-whale-alert-php-3501</link>
      <guid>https://dev.to/tigusigalpa/track-blockchain-whale-activity-in-php-with-whale-alert-php-3501</guid>
      <description>&lt;p&gt;Large on-chain transfers are valuable events for monitoring, analytics, and alerting workflows. A product may need to retrieve a historical transaction, inspect the synchronization status of a blockchain, or notify a team the moment a high-value transfer appears. In PHP, those needs can quickly turn into repetitive work: composing requests, parsing responses, treating money safely, retrying temporary failures, and maintaining a live WebSocket connection.&lt;/p&gt;

&lt;p&gt;That is the problem &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;&lt;strong&gt;whale-alert-php&lt;/strong&gt;&lt;/a&gt; is designed to solve. It is an unofficial, MIT-licensed PHP client for the Whale Alert Enterprise API, with coverage for documented REST endpoints and real-time WebSocket alerts. Rather than exposing a loose collection of arrays and request helpers, the library presents a typed, immutable, and framework-friendly interface for PHP applications. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; &lt;code&gt;whale-alert-php&lt;/code&gt; is an independent, unofficial SDK. It is not affiliated with, endorsed by, or sponsored by Whale Alert. A valid API key is required for authenticated API operations. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why a dedicated PHP SDK matters
&lt;/h2&gt;

&lt;p&gt;Integrating an external blockchain-data API is not difficult in the narrowest sense: an HTTP request can return JSON in a few lines. The engineering work begins after that request. Production code must decide how to represent token quantities, distinguish a missing API key from a rate-limit response, follow a paginated result safely, and recover from a short-lived network or provider failure.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;whale-alert-php&lt;/code&gt; packages those concerns into a focused client. Monetary values and fees are deliberately returned as strings rather than PHP floats, protecting applications from silent precision loss. API responses are mapped to immutable DTOs, while failures are exposed through typed exceptions. The client can use any PSR-18 compatible HTTP implementation, and Laravel applications can opt into a service provider, configuration publishing, a facade, and dependency injection. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capability&lt;/th&gt;
&lt;th&gt;What the SDK provides&lt;/th&gt;
&lt;th&gt;Practical benefit&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;REST API access&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Methods for supported blockchains, blockchain status, transactions, blocks, and address transactions. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Build historical views and data-enrichment workflows without hand-writing endpoint wrappers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Real-time WebSocket events&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Alert and social-event streaming, subscriptions, decoded messages, ping/pong keep-alives, and optional reconnection. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;React to relevant on-chain activity as it arrives.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Typed, immutable DTOs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Structured response objects instead of ad hoc response arrays. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Improve autocomplete, readability, and confidence when integrating the API.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Precision-aware amounts&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fees and monetary fields remain strings. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Avoid float-rounding surprises in financial or analytical code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Resilience controls&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Configurable exponential-backoff retries for idempotent GET requests on HTTP 429 and 5xx responses. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Make short-lived API or network failures less disruptive.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Laravel integration&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Optional service provider, facade, configuration publishing, and singleton client binding. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;td&gt;Add the SDK naturally to Laravel projects without sacrificing dependency injection.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Install it with Composer
&lt;/h2&gt;

&lt;p&gt;The fastest path is a standard Composer install:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require tigusigalpa/whale-alert-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package uses Guzzle by default, but its PSR-18 design lets an application substitute another compatible HTTP client when needed. That is useful for teams with an existing HTTP stack or a deliberate preference for a different client implementation. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with the REST API
&lt;/h2&gt;

&lt;p&gt;The following example creates a client, lists the blockchains supported by the public status endpoint, fetches Ethereum synchronization status, and retrieves a page of transactions. The code follows the library's documented API surface. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\Config&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\WhaleAlertClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;apiKey&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WHALE_ALERT_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;maxRetries&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WhaleAlertClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$config&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Public endpoint: an API key is not required here.&lt;/span&gt;
&lt;span class="nv"&gt;$chains&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSupportedBlockchains&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$chains&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$chain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$chain&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;': '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;implode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$chain&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSymbols&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Authenticated endpoint: supply a valid API key.&lt;/span&gt;
&lt;span class="nv"&gt;$status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBlockchainStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ethereum'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ethereum: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStartHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getEndHeight&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;listTransactions&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ethereum'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'start_height'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getStartHeight&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'limit'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTransactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Transaction &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$tx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getHash&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fee: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$tx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getFee&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$tx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getFeeSymbol&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key detail is not merely that the calls are concise. The return values are DTOs, so readers of the code can see that a transaction hash and fee are part of an explicit API contract. Since fee values are strings, the application can display them as received or use a decimal-math library for precise calculations rather than implicitly converting them to floats. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Make pagination a safe default
&lt;/h2&gt;

&lt;p&gt;Pagination is often treated as a trivial concern until it becomes a security or reliability issue. List calls return a &lt;code&gt;TransactionPage&lt;/code&gt; that contains the current transactions and an optional next URL. When the next page exists, the client can follow it with &lt;code&gt;listTransactionsNext()&lt;/code&gt;. The SDK validates next URLs against the configured base origin, which avoids accidentally following an arbitrary URL returned in a response. Equivalent helpers are available for address-transaction pagination. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getNext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$nextPage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;listTransactionsNext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$page&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getNext&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$nextPage&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getTransactions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$tx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$tx&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getHash&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For applications that process large transaction sets, this small design choice keeps the calling code readable while retaining an important validation boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stream real-time alerts with WebSockets
&lt;/h2&gt;

&lt;p&gt;Polling has its place, but a real-time alerting product should not have to wait for the next scheduled request. &lt;code&gt;whale-alert-php&lt;/code&gt; includes a WebSocket client that lets an application connect, subscribe to alerts, register handlers, and enter a read loop. Automatic reconnection is opt-in: configure &lt;code&gt;maxReconnects&lt;/code&gt; with a value greater than zero when that behavior is appropriate for the application. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a compact example that listens for Ethereum alerts with a minimum USD value of 500,000:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\WebSocket\AlertSubscription&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\WebSocket\Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\WebSocket\EventType&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'WHALE_ALERT_API_KEY'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$wsUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'wss://leviathan.whale-alert.io/ws?api_key=%s'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nv"&gt;$apiKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$wsUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;maxReconnects&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nc"&gt;EventType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Alert&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Blockchain: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'blockchain'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Alert: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$message&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'text'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;onError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Throwable&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;fwrite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;STDERR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Connection issue: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;subscribeAlerts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AlertSubscription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'eth-whale-watch'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;blockchains&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'ethereum'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;minValueUsd&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a useful foundation for an internal operations feed, a Discord or Telegram notification bridge, a data-ingestion worker, or a real-time dashboard. The SDK takes responsibility for the WebSocket protocol mechanics—connection, subscription management, message decoding, keep-alives, and optional reconnection—so the application can focus on what should happen after an alert is received. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Handle failures intentionally
&lt;/h2&gt;

&lt;p&gt;A reliable integration should make it easy to tell a configuration mistake from a temporary upstream problem. The SDK maps API failures to named exception types, including &lt;code&gt;UnauthorizedException&lt;/code&gt; for an invalid or missing credential, &lt;code&gt;RateLimitException&lt;/code&gt; for HTTP 429 responses, &lt;code&gt;NotFoundException&lt;/code&gt;, validation errors, and server-side errors. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\Exceptions\ApiException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\Exceptions\RateLimitException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\Exceptions\UnauthorizedException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getBlockchainStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ethereum'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UnauthorizedException&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Check WHALE_ALERT_API_KEY.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RateLimitException&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Use the provider's suggested retry timing when present.&lt;/span&gt;
    &lt;span class="nv"&gt;$retryAfter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRetryAfter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApiException&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Log or handle other API-specific failures.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The retry strategy is intentionally conservative. It is disabled by default, applies only to idempotent GET requests, retries rate-limit and 5xx responses, uses exponential backoff with jitter, and can honor a &lt;code&gt;Retry-After&lt;/code&gt; header. The library also redacts &lt;code&gt;api_key&lt;/code&gt; values from error excerpts and does not log the key itself. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration option&lt;/th&gt;
&lt;th&gt;Default&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;apiKey&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Empty string&lt;/td&gt;
&lt;td&gt;Sets the credential used for authenticated endpoints. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;timeout&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;30&lt;/code&gt; seconds&lt;/td&gt;
&lt;td&gt;Defines the HTTP timeout. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxRetries&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enables retry attempts for eligible GET requests when set above zero. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;retryDelayMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;500&lt;/code&gt; ms&lt;/td&gt;
&lt;td&gt;Sets the initial delay used by exponential backoff. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;retryMaxDelayMs&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;10000&lt;/code&gt; ms&lt;/td&gt;
&lt;td&gt;Caps the retry delay. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  A natural fit for Laravel
&lt;/h2&gt;

&lt;p&gt;For Laravel projects, the optional &lt;code&gt;WhaleAlertServiceProvider&lt;/code&gt; can publish a &lt;code&gt;config/whale-alert.php&lt;/code&gt; file and register &lt;code&gt;WhaleAlertClient&lt;/code&gt; as a singleton. Developers can then either use the &lt;code&gt;WhaleAlert&lt;/code&gt; facade or request &lt;code&gt;WhaleAlertClient&lt;/code&gt; through dependency injection. That offers a clean path from a simple prototype to maintainable application code. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Tigusigalpa\WhaleAlert\WhaleAlertClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;WhaleAlertClient&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$chains&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getSupportedBlockchains&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chains.index'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chains'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The project also contains runnable REST and WebSocket examples plus PHPUnit tests covering the REST client, WebSocket client, DTOs, and error handling. Those resources make it easier to evaluate the package before embedding it in a broader workflow. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build the blockchain feature, not the plumbing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;whale-alert-php&lt;/code&gt; is for PHP teams that want to work with Whale Alert Enterprise API data without rebuilding the client layer from scratch. Its value is in the details that application code should not need to rediscover: PSR-18 flexibility, immutable typed responses, string-safe monetary fields, explicit error types, controlled retries, guarded pagination, and real-time WebSocket support. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you are building a PHP or Laravel tool that monitors, enriches, visualizes, or routes blockchain-transfer events, install the SDK, review the examples, and tailor the subscriptions and request flow to your use case. Visit the &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for the complete README, source code, and release information. &lt;a href="https://github.com/tigusigalpa/whale-alert-php" rel="noopener noreferrer"&gt;1&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>blockchain</category>
      <category>bitcoin</category>
    </item>
    <item>
      <title>Supercharge Your Algorithmic Trading with CoinQuant PHP: The Ultimate SDK for Laravel and PHP 8.1+</title>
      <dc:creator>Igor</dc:creator>
      <pubDate>Tue, 21 Jul 2026 18:35:38 +0000</pubDate>
      <link>https://dev.to/tigusigalpa/supercharge-your-algorithmic-trading-with-coinquant-php-the-ultimate-sdk-for-laravel-and-php-81-535j</link>
      <guid>https://dev.to/tigusigalpa/supercharge-your-algorithmic-trading-with-coinquant-php-the-ultimate-sdk-for-laravel-and-php-81-535j</guid>
      <description>&lt;p&gt;Are you tired of wrestling with raw cURL requests, parsing Server-Sent Events (SSE) by hand, and building complex polling loops just to interact with trading APIs? If you are a PHP developer or a Laravel enthusiast looking to dive into algorithmic trading, your life is about to get a whole lot easier. Meet &lt;a href="https://github.com/tigusigalpa/coinquant-php" rel="noopener noreferrer"&gt;&lt;strong&gt;coinquant-php&lt;/strong&gt;&lt;/a&gt;, the official PHP and Laravel SDK for the CoinQuant Public API.&lt;/p&gt;

&lt;p&gt;CoinQuant is revolutionizing the way we approach algorithmic trading. It takes a trading idea described in plain English and turns it into a backtestable strategy using advanced AI. However, integrating such powerful tools into your own applications can often be a daunting task. That is where &lt;code&gt;coinquant-php&lt;/code&gt; steps in. This robust SDK wraps the public API, providing a seamless, developer-friendly experience that lets you focus on what really matters: building profitable trading strategies.&lt;/p&gt;

&lt;p&gt;In this comprehensive guide, we will explore why &lt;code&gt;coinquant-php&lt;/code&gt; is a game-changer for PHP developers, delve into its standout features, and show you how to get started in minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Choose CoinQuant PHP?
&lt;/h2&gt;

&lt;p&gt;The landscape of algorithmic trading is often dominated by Python, but PHP remains a powerhouse for web development, especially with frameworks like Laravel. &lt;code&gt;coinquant-php&lt;/code&gt; bridges the gap, allowing PHP developers to leverage the cutting-edge AI capabilities of CoinQuant without leaving their preferred ecosystem.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Built for Modern PHP
&lt;/h3&gt;

&lt;p&gt;The library is designed for the modern era of PHP. It requires PHP 8.1+ and embraces strict typing, ensuring your code is robust and free from legacy baggage. Whether you are building a standalone script or a complex enterprise application, the SDK provides a solid foundation.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. First-Class Laravel Integration
&lt;/h3&gt;

&lt;p&gt;If you are using Laravel 10, 11, 12, or 13, you are in for a treat. The package auto-registers its &lt;code&gt;ServiceProvider&lt;/code&gt; and &lt;code&gt;Facade&lt;/code&gt;, meaning there is absolutely zero manual wiring required. You can simply install the package and start using the &lt;code&gt;CoinQuant&lt;/code&gt; facade anywhere in your controllers, jobs, or commands. It also binds the client as a singleton for elegant dependency injection.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Comprehensive API Coverage
&lt;/h3&gt;

&lt;p&gt;The SDK doesn't just cover the basics; it provides access to all 37 public endpoints of the CoinQuant API. This includes health checks, chat interactions, strategy generation, versioning, backtesting, reports, templates, credit management, and even community features like leaderboards. Everything you need is right at your fingertips.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Effortless Streaming via Generators
&lt;/h3&gt;

&lt;p&gt;One of the most powerful features of CoinQuant is its AI engine, which responds over Server-Sent Events (SSE). Handling SSE in PHP can be tricky, but the SDK abstracts this away completely. It reads the stream and classifies events into categories like &lt;code&gt;chat&lt;/code&gt;, &lt;code&gt;strategy&lt;/code&gt;, &lt;code&gt;report&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, or &lt;code&gt;unknown&lt;/code&gt;. You can even iterate over the generator directly to stream tokens to a browser in real-time, creating a highly responsive user experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Simplified Backtesting
&lt;/h3&gt;

&lt;p&gt;Backtesting is the core of algorithmic trading, and &lt;code&gt;coinquant-php&lt;/code&gt; makes it incredibly simple. The &lt;code&gt;createBacktestAndWait()&lt;/code&gt; method is a one-call solution that submits the run, polls until completion, and returns the final results along with CSV exports for deep analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Actionable Exceptions
&lt;/h3&gt;

&lt;p&gt;Debugging API integrations can be frustrating, but not with this SDK. Any non-2xx response throws a &lt;code&gt;CoinQuantException&lt;/code&gt; that carries the HTTP status, a unique &lt;code&gt;request_id&lt;/code&gt;, a machine-readable &lt;code&gt;code&lt;/code&gt;, and a descriptive message. This makes error handling and troubleshooting a breeze.&lt;/p&gt;




&lt;h2&gt;
  
  
  Getting Started: From Zero to Backtest
&lt;/h2&gt;

&lt;p&gt;Let's walk through how easy it is to get started with &lt;code&gt;coinquant-php&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;

&lt;p&gt;First, pull the package in via Composer. Guzzle, the underlying HTTP client, is installed automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require tigusigalpa/coinquant-php
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration
&lt;/h3&gt;

&lt;p&gt;CoinQuant uses a JWT bearer token for authentication. You can generate one from the Settings menu in the CoinQuant web app. Keep this token secure in an environment variable (&lt;code&gt;COINQUANT_TOKEN&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For standalone PHP:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;CoinQuant\CoinQuantClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CoinQuantClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'COINQUANT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nv"&gt;$credits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getCredits&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Available credits: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$credits&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'available_credits_total'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Laravel, simply add the token to your &lt;code&gt;.env&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;COINQUANT_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_token_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you are ready to use the facade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;CoinQuant\Laravel\Facades\CoinQuant&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$credits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoinQuant&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;getCredits&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Workflow: Idea to Metrics
&lt;/h3&gt;

&lt;p&gt;The true power of the SDK shines when you use it to turn an idea into a fully backtested strategy. Here is a complete workflow in just a few lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Describe the idea and let the AI draft a strategy.&lt;/span&gt;
&lt;span class="nv"&gt;$res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Long BTCUSDT when price crosses above the 200 EMA on 1h, exit on cross below.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Materialize it if it came back schema-only.&lt;/span&gt;
&lt;span class="nv"&gt;$versionId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$res&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;strategyVersionId&lt;/span&gt;
    &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;finalizeChat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$res&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;chatId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'EMA 200 Crossover'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="s1"&gt;'latest_version'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// 3. Backtest and wait for the verdict.&lt;/span&gt;
&lt;span class="nv"&gt;$outcome&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;createBacktestAndWait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$versionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// 4. Analyze the results&lt;/span&gt;
&lt;span class="nb"&gt;print_r&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$outcome&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'results'&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="s1"&gt;'metrics'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, we simply ask the AI to create a strategy based on the 200 EMA crossover. The SDK handles the streaming response, materializes the strategy blueprint into a backtestable version, runs the backtest, and polls for the results. It is algorithmic trading made remarkably accessible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Advanced Usage: Streaming and Customization
&lt;/h2&gt;

&lt;p&gt;For developers who need more control, &lt;code&gt;coinquant-php&lt;/code&gt; delivers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-Time Streaming
&lt;/h3&gt;

&lt;p&gt;If you are building a user interface and want to show the AI's thought process in real-time, you can iterate the generator directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;CoinQuant\Streaming\StreamEvent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$client&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;streamPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Explain the RSI indicator.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nc"&gt;StreamEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TYPE_CHAT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Flush to the client in real time&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom HTTP Clients
&lt;/h3&gt;

&lt;p&gt;Under the hood, the SDK uses Guzzle. If you need to configure retries, route traffic through a proxy, or add custom headers for tracing, you can pass your own configured Guzzle client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;GuzzleHttp\Client&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;GuzzleClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nv"&gt;$http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;GuzzleClient&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'timeout'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'headers'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'COINQUANT_TOKEN'&lt;/span&gt; &lt;span class="p"&gt;)],&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CoinQuantClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'COINQUANT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;$http&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;coinquant-php&lt;/code&gt; SDK is a masterclass in developer experience. By abstracting away the complexities of the CoinQuant API, SSE parsing, and asynchronous polling, it empowers PHP developers to build sophisticated trading applications with unprecedented speed and ease.&lt;/p&gt;

&lt;p&gt;Whether you are a solo developer exploring algorithmic trading or a team building a comprehensive financial platform on Laravel, this SDK provides the tools you need to succeed. The strict typing, elegant Laravel integration, and comprehensive endpoint coverage make it a joy to use.&lt;/p&gt;

&lt;p&gt;Ready to turn your trading ideas into reality? Head over to the &lt;a href="https://github.com/tigusigalpa/coinquant-php" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;, give it a star, and start building today!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Happy Coding and Happy Trading!&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Repository Author:&lt;/strong&gt; Igor Sazonov (&lt;a href="https://github.com/tigusigalpa" rel="noopener noreferrer"&gt;@tigusigalpa&lt;/a&gt;)&lt;strong&gt;License:&lt;/strong&gt; MIT&lt;/p&gt;

</description>
      <category>php</category>
      <category>laravel</category>
      <category>coinquant</category>
      <category>cryptocurrency</category>
    </item>
  </channel>
</rss>
