solver

API documentationOpen solver ↗

POST /v1/solve

Accepts an image and returns a JSON plate solution. No API key required. Images are processed in memory and are not stored.

Choose either request format below. Both require a Content-Length header containing the total body size in bytes; chunked uploads are unsupported. Set SOLVER_URL to the service's origin in the shell examples.

Multipart request

Header: Content-Type
multipart/form-data; boundary=...
Body field: image
Required file part containing the image bytes.
Body field: options
Optional text part containing a JSON object. Defaults to {}. Do not send options in a header for this format.
curl "$SOLVER_URL/v1/solve" \
  -F 'image=@field.fits' \
  -F 'options={"hints":{"fov_width_deg":{"min":2,"max":3}}}'

Omit the options field for a blind solve.

Raw image request

Header: Content-Type
application/octet-stream
Header: X-Solver-Options
Optional JSON object, identical to the multipart options field. Defaults to {}.
Body
Image file bytes only; no JSON wrapper, base64, or multipart fields.
curl "$SOLVER_URL/v1/solve" \
  -H 'Content-Type: application/octet-stream' \
  -H 'X-Solver-Options: {"hints":{"fov_width_deg":{"min":2,"max":3}}}' \
  --data-binary @field.fits

Omit X-Solver-Options for a blind solve.

Upload limits

JPEG, PNG, TIFF, or mono FITS: 32 MiB, 64 MP, 16,384 pixels per side. Options JSON: 8 KiB. Resize larger files before calling the API; only the web UI prepares them automatically.

Options JSON

The following fields belong inside the options JSON object, supplied in the body or header as specified above.

hints.fov_width_deg
Horizontal width: {"min":2,"max":3}, in degrees (0.05–150).
hints.focal_length_px
Known focal length in pixels (50–1,000,000), instead of FOV.
hints.prior_solution
Copy the complete previous solution object. Tries nearby stars first, then falls back to acquisition. Omit FOV/focal hints; clear the prior after resizing, cropping, rotating, or changing optics.
include_sky_context
true adds regional maps for smaller fields. Default: false.
Advanced options

hints.max_motion_deg: 0.001–2 with a prior (default 0.25). search.mode: auto (default), blind (skip prior), or local_only (prior only). search.deadline_ms: 100–30,000, capped by the server search budget (default 5,000 ms). Completed uploads may wait for a worker (default six waiting positions, up to 15 seconds). Responses include scheduling with queue wait and effective budget. Full or expired queues return HTTP 429 with Retry-After. pixel_encoding: auto (default), linear, or srgb; FITS keeps native intensities. Unknown or conflicting options are rejected.

Response

Header: Content-Type: application/json. Body: a JSON object. HTTP 200 has an outcome: solved, no_match, or budget_exhausted. Only solved includes a solution.

solution contains ICRS ra_deg, dec_deg, roll_deg, focal_length_px, width, and height, plus fitted camera parameters when available. Preserve the entire object for the next request.

A recovered camera may include solution.axis_y_multiplier in [0.99, 1.01]; absence means 1. Its mapping is canonical_y = height/2 + (image_y - height/2) * axis_y_multiplier, applied before inverse Brown distortion. Forward projection applies the inverse mapping after Brown distortion. Principal points, matches and overlays remain in uploaded-image coordinates. focal_length_px is the canonical focal length; center_pixel_scale_arcsec measures the horizontal scale. Corrected solutions also report center_pixel_scale_y_arcsec and fov_height_deg. Preserve the multiplier when reusing a prior.

matches provides measured x,y, predicted positions, residuals, and available star names. constellations provides projected lines and label anchors. These already account for accepted distortion. Pixels start at the top left, with centers at (0.5, 0.5), in the uploaded image's dimensions. Quality and processing time appear in rms_residual_px, matched_stars, and timings_ms.

request_timings_ms.upload measures server receipt of the request through the last upload byte; request_timings_ms.processing measures worker dispatch through its result, including communication. Queue time is separate in scheduling.queue_wait_ms. Worker phase timings may overlap; do not sum refinement and annotation subtimers into the solve total.

coordinate_grid and deep_sky_objects contain distortion-aware pixel outlines; DSO sizes are approximate catalog extents, not detections. sky_footprint contains the image boundary in RA/Dec degrees. Optional sky_context maps use 600 × 600 pixel coordinates.

Errors & retries

Errors return {"error":"message"}: 400 invalid request, 403 disallowed host/origin, 408 upload timeout, 411 missing length, 413 oversized body, 422 unsupported image, or 503 worker unavailable. Proxies may return non-JSON errors; check status and Content-Type before parsing.

429 means busy. The response header Retry-After: 1 specifies a one-second wait; retry with backoff. Jobs are not queued. Allow time for upload and processing; an unsolved result is not an HTTP error.

GET /healthz reports readiness, limits, and available workers. Browser calls must use the same origin; cross-origin access is not enabled.

Usage & performance

Usage & performance shows submissions, outcomes, processing-time distribution, throughput, duty cycle and capacity rejections. GET /v1/metrics returns its cached Clio query for the last hour, six hours and 24 hours. Check status, queried_at, partial and delivery_loss_observed before using the numbers. Refreshes run about once per minute; unavailable data retains the previous result with an explicit status. GET /healthz includes local telemetry delivery health.