Code Examples

Complete, runnable examples. The only tricky part of this API is the request signature — sign the exact raw body string you send, joined to the millisecond timestamp with a newline. Every example below does exactly that.

signature = "sha256=" + hex( HMAC_SHA256( signing_secret, timestamp + "\n" + rawBody ) )

Node.js — create a placement

const crypto = require('crypto');

const API_KEY        = process.env.BLASTS_API_KEY;        // "Bearer" credential
const SIGNING_SECRET = process.env.BLASTS_SIGNING_SECRET; // HMAC secret
const ENDPOINT       = 'https://api.blasts.app/api/ping/ads/placement';

async function blastsAds(bodyObj, idempotencyKey) {
  const rawBody   = JSON.stringify(bodyObj);          // serialize ONCE
  const timestamp = Date.now();                        // Unix ms
  const signature = 'sha256=' + crypto
    .createHmac('sha256', SIGNING_SECRET)
    .update(`${timestamp}\n${rawBody}`)                // newline separator
    .digest('hex');

  const res = await fetch(ENDPOINT, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${API_KEY}`,
      'X-Blasts-Timestamp': String(timestamp),
      'X-Blasts-Signature': signature,
      ...(idempotencyKey ? { 'Idempotency-Key': idempotencyKey } : {})
    },
    body: rawBody                                      // send the SAME string you signed
  });
  return res.json();
}

// Create: 25-mile radius around Newark NJ, business recipients, founders/owners.
const thirtyDays = 30 * 24 * 60 * 60 * 1000;
blastsAds({
  action: 'create',
  deal: {
    deal_id: 'newark_founders_q3',
    expires_at: Date.now() + thirtyDays,
    payout_weight: 60,
    creative: {
      copy: 'Free payroll setup for NJ small businesses — offer ends this quarter.',
      url: 'https://advertiser.example.com/nj-payroll'
    },
    snap_badge: { shape: 'pill', color: '#1E1E1E', label: 'AD' },
    targeting: {
      target_mode: 'business',
      geo_radius: [{ zip: '07104', miles: 25, branch: 'business' }],
      business_filters: { primary_roles: ['founder_owner'], org_sizes: ['solo', '2_10'] }
    }
  }
}, 'newark-founders-q3-v1').then(console.log);
// → { ok: true, deal_id: 'p_yourpartnerid_newark_founders_q3', ad: { … } }

Python — pull the ledger

import hashlib, hmac, json, os, time
import urllib.request

API_KEY        = os.environ['BLASTS_API_KEY']
SIGNING_SECRET = os.environ['BLASTS_SIGNING_SECRET'].encode()
ENDPOINT       = 'https://api.blasts.app/api/ping/ads/placement'

def blasts_ads(body_obj):
    raw_body  = json.dumps(body_obj, separators=(',', ':'))   # serialize ONCE
    timestamp = str(int(time.time() * 1000))                  # Unix ms
    payload   = f'{timestamp}\n{raw_body}'.encode()           # newline separator
    signature = 'sha256=' + hmac.new(SIGNING_SECRET, payload, hashlib.sha256).hexdigest()

    req = urllib.request.Request(
        ENDPOINT,
        data=raw_body.encode(),                               # same bytes you signed
        method='POST',
        headers={
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {API_KEY}',
            'X-Blasts-Timestamp': timestamp,
            'X-Blasts-Signature': signature,
        },
    )
    with urllib.request.urlopen(req) as res:
        return json.load(res)

ledger = blasts_ads({'action': 'ledger'})
print(ledger['month'], ledger['month_to_date_sends'], '/', ledger['monthly_send_cap'])
for d in ledger['deals']:
    t = d['totals']
    print(d['deal_id'], 'sends:', t['sends'], 'views:', t['views'], 'clicks:', t['clicks'])

cURL / bash — list live placements

#!/usr/bin/env bash
API_KEY="$BLASTS_API_KEY"
SIGNING_SECRET="$BLASTS_SIGNING_SECRET"
ENDPOINT="https://api.blasts.app/api/ping/ads/placement"

BODY='{"action":"list"}'
TS="$(($(date +%s) * 1000))"

# HMAC-SHA256 over "\n" — printf '%s\n%s' produces exactly that.
SIG="sha256=$(printf '%s\n%s' "$TS" "$BODY" \
  | openssl dgst -sha256 -hmac "$SIGNING_SECRET" -hex \
  | sed 's/^.*= //')"

curl -sS -X POST "$ENDPOINT" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Blasts-Timestamp: $TS" \
  -H "X-Blasts-Signature: $SIG" \
  --data-raw "$BODY"

Node.js — update, then retract

// Raise the bid and refresh the expiry (placements live max 30 days per write):
await blastsAds({
  action: 'update',
  deal_id: 'p_yourpartnerid_newark_founders_q3',
  deal: {
    payout_weight: 85,
    expires_at: Date.now() + 30 * 24 * 60 * 60 * 1000
  }
});

// Campaign over — pull it. Out of rotation within ~60 seconds:
await blastsAds({
  action: 'delete',
  deal_id: 'p_yourpartnerid_newark_founders_q3'
});

Common signature mistakes

Re-serializing after signing. If your HTTP client re-encodes the JSON (different key order, added whitespace), the bytes on the wire differ from the bytes you signed → invalid_signature. Always pass the pre-serialized string as the body.

Seconds instead of milliseconds. X-Blasts-Timestamp is Unix milliseconds. A seconds value looks ~56 years old → timestamp_expired.

Wrong separator. The signing payload joins timestamp and body with a newline (\n) — not a dot, not nothing.

Missing the sha256= prefix. The header value is sha256=<hex> (a bare hex digest is also accepted, but the prefix form is canonical).

Stuck?

Full field-by-field contract in the API Reference, or email AL@SavingsSites.com — sandbox credentials available for integration testing.