NEW: Live arbitrage across 10+ prediction markets.Arbitrage →
← Index
APISep 17, 2026

Prediction Market Arbitrage SDK: TypeScript Quickstart

Prediction Market Arbitrage SDK: TypeScript Quickstart

The Short Answer

The fastest TypeScript path to prediction market arbitrage is to install the Predictefy SDK, create one client with a read-scoped API key and call client.router.fetchArbitrage({ contracts: 100, executableOnly: true }). The SDK handles authentication, normalized venue responses and typed errors across 15+ served venues. Your application still has to preserve timestamps, inspect rejection reasons and keep trading separate from the scan.

Key Takeaways

  • The current TypeScript SDK release is @predictefy/sdk@1.0.0-beta.9, verified on 24 September 2026.
  • It is an ESM package with bundled types for supported modern Node versions.
  • client.router.fetchArbitrage is the assessed cross-venue surface; client.fetchDiscrepancies returns indicative differences.
  • Set contracts to the size you intend to evaluate because depth changes the average cost.
  • Use typed errors to distinguish invalid access, rate limits, unsupported capabilities and temporary platform failures.
  • Pin the beta version and test upgrades before changing a production lockfile.

What is a prediction market arbitrage SDK?

It is a client library that turns an arbitrage API into typed methods. Instead of constructing URLs, headers and response envelopes by hand, the application calls a method and receives normalized objects or a documented error class.

The SDK does not create the economic opportunity. It reduces integration risk around the data needed to evaluate one. A useful SDK should make these boundaries obvious:

  • Market discovery is different from market matching.
  • A price discrepancy is different from assessed arbitrage.
  • An assessed snapshot is different from a reserved fill.
  • Reading data is different from signing and submitting an order.

Predictefy preserves those boundaries through separate client surfaces rather than placing every action behind a generic trade method.

What do you need before installing the SDK?

You need a Predictefy API key and a supported Node runtime. The TypeScript client is ESM and currently supports Node >=20.19 <21 or >=22.12. Node 22.12 or newer is the simplest starting point. For how this SDK compares with PMXT's, see the best PMXT alternative.

Create a new project and keep the key in the environment:

mkdir prediction-arb-quickstart
cd prediction-arb-quickstart
npm init -y
npm install @predictefy/sdk@1.0.0-beta.9
npm install -D typescript tsx @types/node

Add ESM and a start command to package.json:

{
  "type": "module",
  "scripts": {
    "start": "tsx src/index.ts"
  }
}

Set the key in the shell rather than committing it:

export PREDICTEFY_API_KEY="pk_live_YOUR_KEY"

On PowerShell, use $env:PREDICTEFY_API_KEY = "pk_live_YOUR_KEY". The SDK sends the value in the bearer authorization header and redacts it from its documented error fields.

How do you make the first cross-venue call?

Create src/index.ts:

import Predictefy from '@predictefy/sdk';

const client = new Predictefy({
  apiKey: process.env.PREDICTEFY_API_KEY,
});

const markets = await client.router.fetchMarkets({
  query: 'election',
  status: 'active',
  limit: 10,
});

for (const market of markets) {
  console.log({
    venue: market.sourceExchange,
    title: market.title,
    asOf: market.asOf,
    source: market.provenance?.source,
  });
}

Run it with npm start. The router searches across served venues and returns normalized markets. Use it to confirm authentication, response shape and the query you want before moving to cross-venue assessment.

Notice that the example prints freshness and provenance with the title. A market record without those fields may be easier to display, but it is harder to trust when a price later becomes part of a signal.

How do you fetch assessed arbitrage in TypeScript?

Call the router-only arbitrage method and specify the contract count:

import Predictefy from '@predictefy/sdk';

const client = new Predictefy({
  apiKey: process.env.PREDICTEFY_API_KEY,
});

const rows = await client.router.fetchArbitrage({
  contracts: 100,
  executableOnly: true,
  limit: 50,
});

if (rows.length === 0) {
  console.log('No row passed every gate at this size.');
} else {
  console.dir(rows, { depth: null });
}

This is intentionally the first version of the program. Inspect the complete typed response before selecting fields for storage or alerts. An empty result is valid and means no row passed the requested filters at that snapshot.

The assessed surface checks real asks, complete depth, open status, verified fees, resolution equivalence and positive net edge. The API can also return non-executable rows with machine-readable reasons. During development, set executableOnly to false so you can see why candidates failed.

What is the difference between discrepancies and arbitrage?

The SDK exposes both because they answer different questions.

const gaps = await client.fetchDiscrepancies({ live: true });

const assessed = await client.router.fetchArbitrage({
  contracts: 100,
  executableOnly: false,
});
MethodQuestionSafe label
fetchDiscrepanciesWhere do matched markets show different prices?Indicative price discrepancy
router.fetchArbitrageDoes the pair pass live execution gates at this size?Assessed row, executable only if flagged

A gap can be useful even when it fails the stricter test. It may identify a stale venue, a thin book or a contract whose rules deserve inspection. Keeping the two methods separate prevents a research signal from being mistaken for a fillable trade.

How do you handle SDK errors correctly?

The SDK exports typed errors. Catch the cases that change application behavior and let unexpected failures reach observability.

import Predictefy, {
  PlatformUnavailableError,
  RateLimitedError,
  UnauthorizedError,
} from '@predictefy/sdk';

const client = new Predictefy({
  apiKey: process.env.PREDICTEFY_API_KEY,
  retryOn429: true,
});

try {
  const rows = await client.router.fetchArbitrage({
    contracts: 100,
    executableOnly: true,
  });
  console.dir(rows, { depth: null });
} catch (error) {
  if (error instanceof UnauthorizedError) {
    throw new Error('Check the API key and its access.');
  }

  if (error instanceof RateLimitedError) {
    console.error('Rate limited. Retry after the documented backoff.');
    process.exitCode = 1;
  } else if (error instanceof PlatformUnavailableError) {
    console.error('The assessed surface is temporarily unavailable.');
    process.exitCode = 1;
  } else {
    throw error;
  }
}

The client retries eligible GET requests once on a retryable 429 by default. It does not automatically retry POST requests, and it honors the server's retryable flag. Application-level retries should use bounded backoff and should never convert a failed assessment into an older cached opportunity.

How do you stream arbitrage with the SDK?

For an alert or terminal that should stay current, use the TypeScript watcher:

const close = client.watchArbitrage(
  ({ frame }) => {
    for (const row of frame.rows) {
      console.log(row.label, row.executable, row.reasons);
    }
  },
  {
    onError: (error) => {
      console.error(error.code, error.message);
    },
  },
);

process.on('SIGINT', () => {
  close();
  process.exit(0);
});

Always provide onError for a long-lived watcher. A quiet entitled stream and a refused subscription can otherwise look the same from the application's main callback.

The stream is the WebSocket counterpart to the REST assessment. It sends a complete selected surface followed by ordered updates. Recompute or replace local state from the documented full frames rather than assuming an update contains an incremental price-level delta.

How should you structure a small arbitrage application?

Keep the first production version boring and inspectable:

ModuleResponsibilityMust not do
ScannerRequest assessed rows and preserve metadataSubmit orders
PolicyApply age, venue, size and edge limitsRewrite API evidence
NotifierSend a compact candidate for reviewCall the signer
ExecutorBuild, sign and submit approved venue ordersAccept unbounded natural-language instructions
Audit logRecord inputs, reasons and decisionsStore raw API keys

This separation lets you run the scanner with a read-scoped key. If you later add execution, use the SDK's explicit execution origin and a separate trade-scoped key. The system should not gain trading authority merely because a developer added one more method call to the research process.

What should you test before deploying?

  1. Empty result: confirm zero passing rows is displayed as no qualified opportunity, not as an outage.
  2. Rejected rows: run with executableOnly: false and preserve every reason.
  3. Stale data: reject records older than the application's policy allows.
  4. Rate limit: verify bounded backoff and visible failure.
  5. Unavailable assessment: fail closed rather than using an old row.
  6. Reconnect: prove the watcher restarts from coherent full state.
  7. Version upgrade: run type checks and fixture tests before moving off the pinned beta.

The quickstart gets a correct response. These tests are what keep that response correct when the service, network or underlying venues stop behaving like the happy path.

Frequently Asked Questions

Which SDK can I use for prediction market arbitrage?

Predictefy's TypeScript SDK provides a normalized router and an assessed cross-venue arbitrage method across 15+ served venues. It also exposes matched-market discrepancies, venue-specific books, typed errors and a WebSocket arbitrage watcher through one client.

What version of the Predictefy TypeScript SDK should I install?

The current npm release verified on 24 September 2026 is @predictefy/sdk@1.0.0-beta.9. Install and pin that exact version for this quickstart. Check the official SDK documentation and test before adopting a newer beta.

Does the SDK automatically place both arbitrage orders?

No. The assessed arbitrage method is a reads operation. Execution lives behind a separate origin, key scope and client surface, and signing stays in the user's process. Keeping those steps separate prevents a scan from becoming an unintended order.

Why does fetchArbitrage require a contract count?

The API must walk live asks far enough to price the intended size. Ten contracts may fill at the best level while one hundred consume several levels and erase the edge. The contract count turns a top-of-book comparison into a bounded depth calculation.

Should I poll REST or use watchArbitrage?

Use REST for a scheduled scan, command-line tool or initial snapshot. Use watchArbitrage for a long-lived terminal or alert service that needs ordered updates. Both consume the assessed surface, and both require the client to handle access errors and stale data explicitly.