AI & automation

CLI automation — scheduled data pipelines and ops workflows

How ops and data teams use the NTHMAP CLI to build scheduled pipelines, export datasets, and integrate into existing infrastructure.

The NTHMAP CLI is a single Python file that wraps the REST API. It's designed to be scriptable: JSON/CSV/TSV output, exit codes, pipe-friendly, environment-driven configuration. If you've ever written a bash script against a REST API, you'll feel at home in 30 seconds.

This article shows six automation patterns that teams have deployed on NTHMAP.

Pattern 1: Morning CSV export

Export yesterday's LNG carrier positions to a CSV for the morning desk meeting.

#!/bin/bash
# /opt/nthmap/morning-lng.sh

set -e
export NTHMAP_API_KEY=ntm_live_...
DATE=$(date +%Y-%m-%d)
OUTDIR=/var/nthmap/exports

nthmap vessels list \
  --types "LNG Carrier" \
  --format csv \
  --fields mmsi,name,flag,dwt,lat,lng,load_pct,est_cargo_mt,destination,speed_knots \
  > "$OUTDIR/lng-$DATE.csv"

# Upload to S3 for the team
aws s3 cp "$OUTDIR/lng-$DATE.csv" s3://desk-data/lng/$DATE.csv

# Generate a summary
COUNT=$(wc -l < "$OUTDIR/lng-$DATE.csv")
LOADED=$(nthmap vessels list --types "LNG Carrier" --min-load 80 --format json | jq length)
echo "LNG snapshot $DATE: $COUNT total, $LOADED loaded" | \
  slack-notify "#lng-desk"

Cron it: 0 6 * * * /opt/nthmap/morning-lng.sh. Done.

Pattern 2: Portfolio exposure monitor

If you manage a portfolio of hulls (insurance, shipping, P&I), you want to know which ones are currently in high-risk zones. Run this every hour:

#!/bin/bash
# /opt/nthmap/check-exposure.sh

export NTHMAP_API_KEY=ntm_live_...
PORTFOLIO=/etc/nthmap/insured-mmsis.txt

# Define risk zones as bboxes
declare -A ZONES=(
  ["red-sea-south"]="41,10,50,16"
  ["hormuz"]="54,25,58,28"
  ["black-sea"]="28,41,42,47"
  ["gulf-of-guinea"]="-5,-5,10,5"
)

for zone in "${!ZONES[@]}"; do
  # Get vessels in the zone
  nthmap vessels list --bbox "${ZONES[$zone]}" --format json > "/tmp/zone-$zone.json"

  # Intersect with our portfolio
  jq --rawfile portfolio "$PORTFOLIO" '
    [.[] | select(.mmsi as $m | $portfolio | split("\n") | index($m))]
  ' "/tmp/zone-$zone.json" > "/tmp/exposure-$zone.json"

  COUNT=$(jq length "/tmp/exposure-$zone.json")
  if [ "$COUNT" -gt 0 ]; then
    echo "ALERT: $COUNT insured vessels in $zone" | \
      mail -s "NTHMAP exposure alert: $zone" underwriting@firm.com
  fi
done

This gives an underwriting team a live read on exposure to war-risk zones with no human effort.

Pattern 3: Data pipeline into BigQuery / Snowflake

For teams that want NTHMAP data in their existing warehouse:

#!/bin/bash
# /opt/nthmap/sync-to-bigquery.sh

export NTHMAP_API_KEY=ntm_live_...
DATE=$(date -u +%Y-%m-%d)
HOUR=$(date -u +%H)

# Snapshot all vessels
nthmap vessels list \
  --format json \
  | jq --arg snap "$DATE-$HOUR" '[.[] | . + {snapshot_time: $snap}]' \
  > "/tmp/vessels-$DATE-$HOUR.json"

# Stream into BigQuery
bq load \
  --source_format=NEWLINE_DELIMITED_JSON \
  --autodetect \
  dataset.vessels_snapshots \
  "/tmp/vessels-$DATE-$HOUR.json"

# Do the same for events, infrastructure
for ds in events infrastructure; do
  nthmap $ds list --format json > "/tmp/$ds-$DATE-$HOUR.json"
  bq load --source_format=NEWLINE_DELIMITED_JSON \
    dataset.${ds}_snapshots \
    "/tmp/$ds-$DATE-$HOUR.json"
done

Run hourly from Airflow or Prefect. You now have a time-series of the global physical world in your warehouse.

Pattern 4: Event-triggered AI analysis

When a new conflict event appears, auto-run an AI flow analysis for the affected region.

#!/bin/bash
# /opt/nthmap/event-watcher.sh — runs every 10 minutes

export NTHMAP_API_KEY=ntm_live_...
STATE_FILE=/var/nthmap/last-event-ids

# Get current conflict events
nthmap events list --types conflict --format json | \
  jq -r '.[] | .id' > /tmp/current-events.txt

# Find new events (in current but not in state file)
comm -23 <(sort -u /tmp/current-events.txt) <(sort -u "$STATE_FILE") > /tmp/new-events.txt

while read -r EVENT_ID; do
  # Get event details
  EVENT=$(nthmap events get $EVENT_ID --format json)
  LAT=$(echo "$EVENT" | jq -r .lat)
  LNG=$(echo "$EVENT" | jq -r .lng)

  # Run AI flow analysis in a 500km box around it
  BBOX="$(python -c "print(f'{$LNG-2.5},{$LAT-2.5},{$LNG+2.5},{$LAT+2.5}')")"
  ANALYSIS=$(nthmap ai flow --bbox "$BBOX")

  # Send to ops channel
  echo "$EVENT\n\n$ANALYSIS" | slack-notify "#ops-alerts"
done < /tmp/new-events.txt

cp /tmp/current-events.txt "$STATE_FILE"

Pattern 5: Saved view as a scheduled query

Saved views in NTHMAP are user-defined combinations of filters + map state. The CLI can re-run a saved view as a query:

# Save a view in the web app, then run it from CLI
nthmap views run "Persian Gulf Watch" --format json > gulf-snapshot.json

# Or list all saved views
nthmap views list

For teams, standardize on a set of named views for recurring queries. You change the definition once in the web app, all the cron jobs pick up the new filters.

Pattern 6: CI/CD integration

If your product depends on NTHMAP data, integrate into your CI:

# .github/workflows/data-freshness.yml
name: NTHMAP data freshness check
on:
  schedule:
    - cron: '0 */4 * * *'
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - run: |
          curl -sSLo /usr/local/bin/nthmap https://nthmap.com/cli/nthmap.py
          chmod +x /usr/local/bin/nthmap
      - env:
          NTHMAP_API_KEY: ${{ secrets.NTHMAP_API_KEY }}
        run: |
          COUNT=$(nthmap vessels list --format json | jq length)
          if [ "$COUNT" -lt 100 ]; then
            echo "::error::Vessel count suspiciously low: $COUNT"
            exit 1
          fi
          echo "$COUNT vessels — data healthy"

Now if the NTHMAP data pipeline has a hiccup, your own CI catches it and alerts you before a customer does.

Tips

  • Set NTHMAP_API_KEY in systemd service files using EnvironmentFile=/etc/nthmap/env, not hardcoded in scripts.
  • Use --format json + jq for anything non-trivial. CSV is for spreadsheets, JSON is for pipelines.
  • Add --profile <name> to ~/.nthmaprc if you use multiple environments (prod, staging, dev).
  • Log responses to /var/log/nthmap/ so you can debug pipeline failures after the fact.
  • Don't poll too aggressively. Most datasets update every 5-30 minutes server-side; polling every 60 seconds just burns rate limits.

Learn more