REST API

For direct HTTP access without the Python client. All endpoints are under https://campfire.hollisakins.com/api/v1/.

Authentication#

All requests require an Authorization header:

bash
# Using API key
curl -H "Authorization: Bearer sk_your_api_key" \
  https://campfire.hollisakins.com/api/v1/objects

# Using JWT access token (from device flow)
curl -H "Authorization: Bearer eyJ..." \
  https://campfire.hollisakins.com/api/v1/objects

Data Endpoints#

GET /objects#

Query objects (cross-program grouped sky positions) with filters.

Query Parameters:

ParameterTypeDescription
programsstringComma-separated program slugs
fieldsstringComma-separated field names
gratingsstringComma-separated grating types
observationsstringComma-separated observation names
redshift_minfloatMinimum redshift
redshift_maxfloatMaximum redshift
redshift_qualitystringComma-separated quality codes
max_snr_minfloatMinimum max SNR
max_snr_maxfloatMaximum max SNR
listsstringComma-separated tag slugs (e.g., lrd,blagn)
inspected_onlybooleanFilter to inspected objects
has_photometrybooleanFilter to objects with photometry
searchstringText search on object_id / member target_ids
rafloatRA for cone search (degrees)
decfloatDec for cone search (degrees)
radiusfloatSearch radius (arcsec)
limitintMax results (default: 1000)
offsetintPagination offset
sortstringSort column (object_id, ra, dec, redshift, redshift_quality, field, n_targets, n_spectra, max_snr, max_exposure_time, distance)
sort_dirstring'asc' or 'desc'

Response:

json
{
  "data": [
    {
      "object_id": "CAMPFIRE-J095850.12+021234.5",
      "ra": 34.1234,
      "dec": -5.4567,
      "redshift": 2.345,
      "redshift_quality": 3,
      "field": "COSMOS",
      "member_targets": [
        {"target_id": "ember_cosmos_p1_12345", "program_slug": "ember-cosmos", "observation": "ember_cosmos_p1", "redshift_auto": 2.345}
      ]
    }
  ],
  "pagination": {"total": 1500, "limit": 1000, "offset": 0}
}

GET /spectra/list#

Flat list of spectra (one row per spectrum) with filters.

Same vocabulary as /objects plus per-spectrum filters (dq_flags_include_any, dq_flags_include_all, dq_flags_exclude). Each row carries spectrum_id, target_id, object_id, grating, fits_path, signal_to_noise, exposure_time, redshift_auto, dq_flags.

GET /spectra#

Get a signed URL for downloading a FITS file.

ParameterTypeDescription
pathstringFITS file path (required)

Response: {"url": "https://..."}

GET /spectrum#

Get spectrum JSON data for plotting.

ParameterTypeDescription
spectrum_idstringStable per-spectrum identifier

Response:

json
{
  "wave": [1.0, 1.1, ...],
  "fnu": [0.5, 0.6, ...],
  "fnu_err": [0.1, 0.1, ...],
  "snr_2d": [[...], [...]],
  "n_spatial": 10,
  "n_wave": 500,
  "profile": [...],
  "profile_fit": [...],
  "profile_pix": [...]
}

GET /redshift-fit#

Get redshift fitting results.

ParameterTypeDescription
target_idstringTarget ID
gratingstringGrating type

Response:

json
{
  "redshift": 2.345,
  "chi2_min": 1.23,
  "confidence": 95.5,
  "z_grid": [0.0, 0.1, ...],
  "chi2_grid": [100, 95, ...],
  "model_wave": [...],
  "model_fnu": [...]
}

GET /metadata#

Get available filter options.

Response:

json
{
  "programs": [
    {"slug": "ember-uds", "program_name": "EMBER-UDS", "pi_name": "...", "is_public": false}
  ],
  "fields": ["COSMOS", "UDS"],
  "gratings": ["PRISM", "G395M"],
  "observations": ["ember_uds_p4"]
}

GET /observations#

List observations with stats.

Response:

json
{
  "observations": [
    {
      "observation": "ember_uds_p4",
      "program_name": "EMBER-UDS",
      "field": "UDS",
      "target_count": 450,
      "spectrum_count": 1350,
      "total_size_bytes": 2147483648
    }
  ]
}

Auth Endpoints#

GET /auth/whoami#

Get current user info. Response: {"user_id": "uuid", "email": "...", "full_name": "..."}

POST /auth/device#

Initiate device flow authorization (for CLI).

Response:

json
{
  "device_code": "...",
  "user_code": "WDJB-MJPQ",
  "verification_uri": "https://campfire.hollisakins.com/cli-auth",
  "verification_uri_complete": "https://campfire.hollisakins.com/cli-auth?code=WDJB-MJPQ",
  "expires_in": 900,
  "interval": 5
}

POST /auth/device/token#

Poll for tokens after user authorization.

Request: {"grant_type": "urn:ietf:params:oauth:grant-type:device_code", "device_code": "..."}

Response (success): {"access_token": "...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "..."}

Response (pending): {"error": "authorization_pending"}

POST /auth/refresh#

Refresh an access token.

Request: {"grant_type": "refresh_token", "refresh_token": "..."}

Response: {"access_token": "...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "..."}


Error Codes#

CodeDescription
200Success
400Invalid parameters
401Invalid/missing authentication
403No access to resource
404Not found
429Rate limited
500Server error

Rate Limits#

  • Standard: 100 requests/minute
  • Burst: Up to 10 concurrent requests