API · v1

L'API SkySolver

Résolvez une photo du ciel nocturne, ou la liste de ses étoiles, et dessinez son annotation, en SVG à poser sur la photo ou en JSON à dessiner vous-même ; ou résolvez une photo de la Lune : les mêmes solveurs et les mêmes annotations que l'atelier. L'API est gratuite et sans clé, et les programmes conçus pour le solveur en ligne d'Astrometry.net, comme N.I.N.A. et KStars, peuvent l'utiliser en changeant une seule adresse : voir comment.

Aperçu

Sans clé
Gratuite et anonyme : ni compte ni clé d'API.
Limite de requêtes
Par adresse cliente, selon le poids de l'endpoint.
Rien de conservé
Images, listes d'étoiles et résultats ne sont ni stockés ni journalisés.
  • URL de base https://skysolver.app/api/v1. Requêtes et réponses sont en JSON (UTF-8) ; l'annotation peut aussi venir en SVG.
  • Quatre endpoints : résoudre une image envoyée en base64, résoudre des positions d'étoiles que vous avez mesurées vous-même, résoudre une photo de la Lune, et annoter une image résolue.
  • CORS est ouvert : vous pouvez appeler l'API depuis une page web.
  • Versions : les changements incompatibles reçoivent un nouveau chemin (/api/v2) ; v1 ne gagne que des champs facultatifs. Le document OpenAPI décrit chaque modèle.
  • Usage raisonnable : le service est gratuit et tourne sur un petit serveur. Pour de gros lots, résolvez des listes d'étoiles que vous détectez vous-même, ou demandez à [email protected].

Démarrage rapide

Résolvez une photo, puis obtenez son annotation en SVG à la taille de la photo, à poser dessus :

shell
# 1. solve: the body is {"image": "<the file in base64>"}
base64 < photo.jpg | jq -Rs '{image: .}' \
  | curl -s https://skysolver.app/api/v1/solve/image \
      -H 'Content-Type: application/json' --data-binary @- > solution.json

# 2. annotate: the whole solution goes in as the sky model
jq '{solution: ., format: "svg"}' solution.json \
  | curl -s https://skysolver.app/api/v1/annotate \
      -H 'Content-Type: application/json' --data-binary @- > annotation.svg

La même chose en JavaScript (Node 18 ou plus récent ; dans un navigateur, seule la lecture du fichier diffère) :

javascript
import { readFile } from "node:fs/promises";

const post = (path, body) => fetch("https://skysolver.app/api/v1" + path, {
  method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
});

// in a browser: FileReader's readAsDataURL(file) gives a data: URL, which works as it is
const image = (await readFile("photo.jpg")).toString("base64");
const solution = await (await post("/solve/image", { image })).json();
if (solution.solved) {
  const answer = await post("/annotate", { solution, time: "2024-10-12T21:34:05+02:00" });
  const svg = await answer.text();
}

Et en Python :

python
import base64, json, urllib.request

API = "https://skysolver.app/api/v1"

def post(path, body):
    req = urllib.request.Request(API + path, data=json.dumps(body).encode(),
                                 headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req) as r:
        return r.read()

with open("photo.jpg", "rb") as f:
    image = base64.b64encode(f.read()).decode()
solution = json.loads(post("/solve/image", {"image": image}))
if solution["solved"]:
    print(solution["center"], solution["scale_arcsec"])
    svg = post("/annotate", {"solution": solution, "format": "svg"})

Limites

Chaque adresse cliente dispose d'un seau de requêtes par endpoint : jusqu'à la rafale d'un coup, rempli à un rythme constant. Une requête au-delà reçoit aussitôt 429 et Retry-After. Chaque réponse porte RateLimit-Limit (la rafale), RateLimit-Remaining, RateLimit-Reset (secondes jusqu'à ce que le seau soit plein) et RateLimit-Policy.

EndpointRafaleRemplissageCorps de requête
POST /solve/image101 par minute28 MB (un fichier de 20 MB en base64)
POST /solve/stars3030 par minute4 MB
POST /solve/moon2010 par minute28 MB (un fichier de 20 MB en base64)
POST /annotate3030 par minute2 MB
GET /health6060 par minute–
  • Images : au plus 20 MB et 120 mégapixels. Le solveur cherche pendant au plus time_limit_ms (1 à 30 s, 12 s par défaut). Une photo de la Lune est résolue à 2048 pixels au plus sur son grand côté (les plus grandes sont réduites d'abord ; la réponse est dans les pixels du fichier) et prend quelques secondes au plus.
  • Listes d'étoiles : 5 à 10 000 étoiles ; les 3 000 plus brillantes sont utilisées.
  • Occupé : le serveur exécute quelques résolutions à la fois (max_concurrent dans health). Au-delà, une requête reçoit aussitôt 429, error: "busy" et Retry-After : attendez ce délai, plus une seconde environ au hasard, et renvoyez-la.
  • GET /health renvoie les limites en vigueur.

Conventions

  • Les coordonnées célestes sont en degrés dans l'ICRS (J2000) : RA 0–360, Dec −90–90.
  • Les pixels se comptent à partir de 0 au centre du pixel en haut à gauche ; x croît vers la droite et y vers le bas. Le coin supérieur gauche de l'image est donc (−0.5, −0.5) et son centre (largeur/2 − 0.5, hauteur/2 − 0.5). FITS compte à partir de 1 : les listes d'étoiles peuvent le dire (pixel_origin), et fits_header suit la convention propre à FITS.
  • Les heures sont en ISO 8601 avec un décalage UTC ou Z : 2024-10-12T21:34:05+02:00. Une heure sans décalage est refusée, jamais devinée.
  • Les erreurs sont {"error": code, "message": text}, avec details pour les champs invalides : voir Erreurs.

Résoudre une image

POST/api/v1/solve/image10 d'un coup · 1 par minute

Détecte les étoiles d'une image et trouve où elle pointe dans le ciel : à l'aveugle, sans indication de position, d'échelle ni de rotation, pour des champs de quelques minutes d'arc aux panoramas de tout le ciel. L'image est décodée et résolue en mémoire, et n'est pas conservée.

Paramètres
image
stringrequis
Le fichier image, encodé en base64 (alphabet standard ou URL-safe ; un préfixe data:image/…;base64, est accepté). Au plus 20 MB décodés et 120 mégapixels. Formats : JPEG, PNG (8/16 bits), TIFF (8/16/32 bits et flottant ; non compressé, LZW, ZIP, PackBits), FITS (entier ou flottant ; gris, RVB ou la mosaïque brute d'une caméra couleur), XISF (non compressé ou zlib), BMP, GIF, TGA et PSD. Les données linéaires (empilements, FITS, XISF) sont étirées avant la détection.
time_limit_ms
integerfacultatif
Combien de temps le solveur peut chercher avant d'abandonner.
Par défaut 12000Autorisé 1000–30000
requête
{
  "image": "/9j/4AAQSkZJRgABAQEASABIAAD…",
  "time_limit_ms": 12000
}

La réponse est la solution, plus image (format, width, height) et capture : quand et où la photo a été prise, d'après ses métadonnées (EXIF, XMP, IPTC, en-têtes FITS, propriétés XISF).

capture
time
object | null
L'heure de prise de vue, ou null quand le fichier n'en a pas.
└utc
string | null
L'instant en UTC : null quand le fichier donne une lecture d'horloge sans son fuseau horaire (EXIF avant 2.31, XMP de Lightroom).
└local
string
La lecture d'horloge telle qu'écrite : l'heure locale de l'appareil pour EXIF, UTC pour FITS.
└utc_offset
string | null
Le décalage de l'horloge, +02:00 ; null quand il est inconnu.
└source
string
Où elle a été lue : EXIF DateTimeOriginal, FITS DATE-OBS, XISF Observation:Time:Start…
└exposure_s
number
La pose (ou, pour un empilement, du premier début à la dernière fin) quand elle est connue ; l'heure est alors son milieu.
└note
string
Quelque chose à vérifier : un empilement daté par l'une de ses images, un appareil dont le fuseau n'était peut-être pas réglé.
└date_only
boolean
true quand le fichier donne une date sans heure.
location
object | null
Où elle a été prise, d'après le GPS ou les mots-clés de site FITS : {lat, lon, height, source}, degrés est et nord, mètres.
other_dates
array
Les autres dates du fichier qui ne sont peut-être pas l'heure de prise de vue (création, modification), de la même forme que time.
réponse (abrégée)
{
  "api_version": "1.0",
  "solved": true,
  "width": 2048,
  "height": 1366,
  "wcs": {
    "ctype": "RA---TAN-SIP",
    "crval": [57.56884791, 30.441625],
    "crpix": [1023.5, 682.5],
    "cd": [[0.0004834903827, 0.008644326623], [-0.008637593318, 0.0004834749004]],
    "sip": { "order": 3, "a": [[0, 2, -1.585329748e-7], …], "b": […], "inv_order": 4, "ap": […], "bp": […] }
  },
  "projection": null,
  "center": { "ra": 57.56884791, "dec": 30.441625 },
  "corners": [{ "name": "top-left", "x": -0.5, "y": -0.5, "ra": 49.44900035, "dec": 38.65911116 }, …],
  "fov": { "width_deg": 17.6401047, "height_deg": 11.79908067 },
  "scale_arcsec": 31.15610913,
  "rotation_deg": 266.7987974,
  "parity": "normal",
  "stats": { "stars": 3294, "matches": 972, "rms_px": 0.3723350842, "log_odds": 3198.350966, … },
  "fits_header": "SIMPLE  =                    T …",
  "matched": [[1988.17, 1290.68, 63.70586, 22.302973, 8.59], …],
  "timing_ms": { "solve": 153.9, "detect": 40.3, "decode": 507.7, "server": 844.4 },
  "image": { "format": "FITS", "width": 2048, "height": 1366 },
  "capture": {
    "time": { "utc": "2025-08-16T21:00:00.000Z", "local": "2025-08-16T23:00:00.000", "utc_offset": "+02:00",
              "source": "FITS DATE-OBS", "exposure_s": 60 },
    "location": { "lat": 52.2297, "lon": 21.0122, "height": 110, "source": "FITS SITELAT/SITELONG" },
    "other_dates": []
  }
}

Résoudre des positions d'étoiles

POST/api/v1/solve/stars30 d'un coup · 30 par minute

Résout à partir de positions d'étoiles que vous avez mesurées vous-même (SExtractor, SEP, photutils, le StarDetector de PixInsight, vos propres centroïdes) : l'image ne quitte jamais votre machine, et la requête fait quelques kilo-octets. C'est ainsi que l'atelier résout.

Paramètres
width
integerrequis
Largeur de l'image en pixels.
Autorisé 8–100000
height
integerrequis
Hauteur de l'image en pixels.
Autorisé 8–100000
stars
arrayrequis
Positions des étoiles, chacune [x, y], [x, y, flux] ou {"x": …, "y": …, "flux": …}. flux est n'importe quelle mesure positive de brillance (somme, pic, …) : donnez-le pour chaque étoile ou pour aucune, et sans lui listez les étoiles les plus brillantes d'abord. 30 à 300 étoiles suffisent largement ; les 3 000 plus brillantes sont utilisées. Les positions doivent être dans l'image.
Autorisé 5–10000 items
pixel_origin
integerfacultatif
0 : le centre du premier pixel est (0, 0), comme dans numpy et OpenCV. 1 : c'est (1, 1), comme dans FITS, SExtractor, astrometry.net et DS9. Les réponses sont toujours en base 0.
Par défaut 0Autorisé 0 · 1
time_limit_ms
integerfacultatif
Combien de temps le solveur peut chercher.
Par défaut 12000Autorisé 1000–30000
requête
{
  "width": 6000,
  "height": 4000,
  "stars": [
    [3735.37, 69.14, 51230],
    [2120.04, 1987.51, 48810],
    { "x": 812.6, "y": 3020.9, "flux": 47002 },
    …
  ]
}

La réponse est la solution.

La solution

Les deux endpoints de champ d'étoiles répondent avec cet objet.

champs
solved
boolean
Si une solution a été trouvée. Une photo qui ne peut pas être résolue reste une réponse 200, avec solved: false et une error.
width, height
integer
La taille de l'image en pixels.
center
object | null
Le ciel au milieu de l'image : {ra, dec} en degrés. null quand le milieu d'une image de tout le ciel n'est pas sur le ciel.
corners
array
Les quatre coins extérieurs de l'image, top-left, top-right, bottom-right, bottom-left : chacun {name, x, y, ra, dec}, avec ra et dec à null là où un coin est hors du ciel.
fov
object
{width_deg, height_deg} : la longueur angulaire des lignes médianes horizontale et verticale de l'image.
scale_arcsec
number
Secondes d'arc par pixel au centre.
rotation_deg
number
Où pointe le haut de l'image : degrés du nord vers l'est (0 : le nord en haut, 90 : l'est en haut).
parity
string
Les images en miroir (retournées au traitement, ou par un renvoi coudé) sont résolues aussi.
Autorisé normal · mirrored
wcs
object | null
La solution en TAN ou TAN-SIP : crval (RA, Dec du pixel de référence), crpix (le pixel de référence, en base 0), cd (degrés par pixel) et sip (polynômes de distorsion, termes [p, q, coefficient]). null pour les solutions par projection.
projection
object | null
La solution en modèle de projection, pour les panoramas, fisheye et vues de tout le ciel qu'aucun TAN-SIP n'ajuste ; null sinon. Passez-le, ou la solution entière, à /annotate.
stats
object
stars (détectées ou fournies), matches, rms_px (l'écart des étoiles appariées au modèle), log_odds (le poids de preuve de l'appariement, un logarithme naturel : une solution exige 25 et 10 étoiles appariées), et les détails de la recherche.
fits_header
string | null
La solution TAN-SIP en en-tête FITS WCS (cartes de 80 caractères ; CRPIX en base 1 comme FITS), prête à fusionner dans un fichier FITS. null pour les solutions par projection.
matched
array
Les étoiles du catalogue (Gaia DR3) que la solution a appariées, jusqu'à quelques milliers : [x, y, ra, dec, magnitude] chacune, la magnitude étant le G de Gaia.
timing_ms
object
decode (lecture du fichier), detect, solve, et le total du server pour la requête, en millisecondes.
error
string
Pourquoi il n'y a pas de solution (quand solved est false).
api_version
string
1.0

Résoudre une photo de la Lune

POST/api/v1/solve/moon20 d'un coup · 10 par minute

Trouve la Lune dans une photo et ajuste son disque : à l'aveugle, à n'importe quelle échelle, rotation et parité, à n'importe quelle phase, que le cadre contienne le disque entier, une partie ou un gros plan de quelques cratères. La photo filtrée passe-bande est appariée, à chaque position, rotation et parité, à un mélange de trois cartes fixes de la face visible (l'albédo et ses deux pentes), ce que n'importe quelle direction du Soleil fait du sol ; la fraction de la photo que le mélange explique est la confiance, et le limbe, quand il y en a un, fixe la libration. La réponse place chaque point de la photo sur la Lune : le centre, le rayon et l'orientation du disque, la libration, et le Soleil et la phase lus d'après le terminateur. Les gros plans se résolvent de façon fiable jusqu'à des régions d'environ 250 km de large ; un champ de moins de 150 km environ, un cratère qui remplit le cadre, revient souvent en unsure. L'image est décodée et résolue en mémoire, et n'est pas conservée.

Paramètres
image
stringrequis
La photo, encodée en base64 (alphabet standard ou URL-safe ; un préfixe data:image/…;base64, est accepté). Au plus 20 MB décodés et 120 mégapixels. Formats : JPEG, PNG (8/16 bits), TIFF, WebP, BMP et GIF ; la couleur est convertie en luminance, les données 16 bits étirées par leur pixel le plus brillant. Les pixels sont lus tels que stockés (une orientation EXIF n'est pas appliquée), si bien que la réponse correspond à ce que montre une visionneuse qui l'ignore.
requête
{
  "image": "/9j/4AAQSkZJRgABAQEASABIAAD…"
}
champs
solved
boolean
Si la Lune a été trouvée avec un verdict autre que unsure. Une photo sans Lune reste une réponse 200, avec solved: false et une error.
verdict
string
verified : une réponse sûre qui a passé les recoupements (un gros plan domine nettement le second candidat de la photo, et s'accorde avec le limbe quand il y en a un dans le cadre). plausible : probable mais non confirmée. unsure : non résolue ; la meilleure hypothèse est tout de même rapportée dans view, pour ce qu'elle vaut.
Autorisé verified · plausible · unsure
path
string
Comment elle a été résolue : d'après le limbe du disque (la Lune entière, ou une grande partie, dans le cadre) ou par la recherche de gros plan sur la face visible.
Autorisé disk · closeup · none
confidence
number
La fraction de l'énergie passe-bande de la photo que le modèle d'éclairage explique à la réponse.
Autorisé 0–1
runner_up
number
La même mesure pour la meilleure réponse différente (un autre lieu, rotation, échelle ou parité).
width, height
integer
La taille de l'image en pixels. Toute valeur en pixels ci-dessous est dans ce repère.
view
object | null
Le disque dans la photo, null quand rien n'a été trouvé : cx, cy, R sont le centre et le rayon du disque en pixels (centres de pixels en base 0 ; pour un gros plan ils sont loin hors du cadre) ; rho est l'angle à l'écran du nord lunaire, en degrés antihoraires depuis le haut de l'image ; par vaut 1 pour une vue directe et -1 pour une vue en miroir ; lon0, lat0 sont le point sub-observateur en degrés sélénographiques (longitude positive vers l'est), c'est-à-dire la libration.
scale_km_px
number
Kilomètres par pixel au centre du disque : 1737,4 km divisés par R.
sun
object | null
Le point subsolaire, {lon, lat, score, terminator}, lu d'après la photo : là où sol éclairé et sol sombre se rencontrent dans le cadre, la longitude du Soleil qui prédit le mieux lequel est lequel (lat vaut 0 ; la vraie valeur est toujours à moins de 1,6°) ; score est la fraction des échantillons du cadre qu'elle prédit correctement. Un disque entier sans terminateur est une pleine lune (terminator: false, le Soleil au point sub-observateur). null pour un gros plan sans terminateur : la phase ne peut pas en être déduite.
phase
object | null
D'après view et sun : angle_deg (l'angle de phase : 0 pleine, 180 nouvelle), fraction (éclairée), waxing, colongitude (la colongitude sélénographique du Soleil : 0 au premier quartier, 90 à la pleine lune, 180 au dernier quartier) et name (new moon, waxing crescent, first quarter, waxing gibbous, full moon, waning gibbous, last quarter, waning crescent). null sans sun.
limb
object | null
Le bord brillant du disque tel que mesuré dans la photo, {cx, cy, R, arc, refined, locked} (pixels comme dans view ; arc est la part du cercle qui a été mesurée), null quand aucun limbe n'a été trouvé. locked: true signifie que le cercle de la vue est ce bord ; sinon la texture de surface a placé la sphère elle-même, qui se trouve à l'intérieur du bord débordant d'une Lune surexposée.
frame
object | null
Les coordonnées sélénographiques sous le cadre : center et corners (top-left, top-right, bottom-right, bottom-left), chacun {lon, lat} ou null là où le point est hors du disque (les coins d'une photo du disque entier).
stats
object
hyps (positions essayées par la recherche de gros plan) et octaves (octaves d'échelle explorées) ; tous deux à 0 quand le limbe a tranché.
timing_ms
object
decode, limb, disk, closeup, solve et le total du server, en millisecondes.
image
object
format (tel que le décodeur le nomme : JPEG, PNG, …), width, height.
error
string
Pourquoi il n'y a pas de solution (quand solved est false).
api_version
string
1.0
réponse (un gros plan de Sinus Iridum au lever du Soleil)
{
  "api_version": "1.0",
  "solved": true,
  "verdict": "verified",
  "path": "closeup",
  "confidence": 0.7642,
  "runner_up": 0.0223,
  "width": 1560,
  "height": 1075,
  "view": { "cx": -602.996, "cy": 3686.543, "R": 4399.212, "rho": 305.2046, "par": 1, "lon0": 5.727, "lat0": 4.9752 },
  "scale_km_px": 0.39493,
  "sun": { "lon": 56.5, "lat": 0.0, "score": 0.9505, "terminator": true },
  "phase": { "angle_deg": 50.95, "fraction": 0.815, "waxing": true, "colongitude": 33.5, "name": "waxing gibbous" },
  "limb": null,
  "frame": {
    "center": { "lon": -29.9219, "lat": 46.1421 },
    "corners": [{ "lon": -46.2102, "lat": 39.6896 }, { "lon": -62.682, "lat": 64.4227 }, { "lon": -13.7619, "lat": 52.7875 }, { "lon": -22.696, "lat": 31.4166 }]
  },
  "stats": { "hyps": 864, "octaves": 3 },
  "timing_ms": { "limb": 10.6, "disk": 181.8, "closeup": 145.4, "solve": 337.7, "decode": 5.4, "server": 347.9 },
  "image": { "format": "JPEG", "width": 1560, "height": 1075 }
}

Pour dessiner sur la photo, projetez un point sélénographique (lon, lat) avec la vue : avec p = (cos lat cos lon, cos lat sin lon, sin lat), e_z le même vecteur pour (lon0, lat0), e_y l'axe de la Lune rendu perpendiculaire à e_z et e_x = e_y × e_z : X = par · p·e_x, Y = p·e_y, x = cx + R (X cos ρ − Y sin ρ), y = cy − R (X sin ρ + Y cos ρ) ; le point est sur la face visible quand p·e_z > 0.

Annoter une image résolue

POST/api/v1/annotate30 d'un coup · 30 par minute

Dessine la grille de coordonnées, les constellations, les objets du ciel profond (20 000 : Messier, NGC, IC, Sharpless, Barnard, Lynds, van den Bergh, Caldwell, les catalogues d'amas d'étoiles, nébuleuses planétaires, rémanents de supernova, groupes de galaxies Arp, Hickson et Abell, RCW et Cederblad), les étoiles nommées et désignées et, avec une heure, le Soleil, la Lune, les planètes, astéroïdes et comètes, comme l'Enregistrer l'image de l'atelier les dessine : les mêmes objets, étiquettes et styles. Vous envoyez le modèle de ciel, pas l'image. L'annotation n'est pas découpée au ciel : l'atelier trouve l'horizon dans la photo lui-même.

Paramètres
sky model
one ofrequis
Exactement l'un de corners, wcs, fits_header, projection ou solution : voir Modèles de ciel.
width, height
integerfacultatif
La taille de l'image en pixels. Requise avec corners, wcs et projection ; prise de solution ou des NAXIS1/NAXIS2 de l'en-tête FITS sinon.
Autorisé 8–100000
format
stringfacultatif
svg : un document SVG à la taille de l'image, transparent, à poser sur la photo. json : les objets, corps, lignes et étiquettes placées en données (Sortie JSON).
Par défaut "svg"Autorisé svg · json
time
stringfacultatif
Quand la photo a été prise (ISO 8601 avec un décalage ou Z). Avec elle, le Soleil, la Lune, les planètes et Pluton sont dessinés là où ils étaient (à 1″ près pour 1850–2150), ainsi que les astéroïdes les plus brillants et les comètes (à environ 1″ près pour 1990–2041).
location
objectfacultatif
Où elle a été prise : la Lune se déplace jusqu'à un degré avec le lieu de l'observateur (sa parallaxe). Sans lieu, la Lune est placée comme vue depuis le centre de la Terre.
└lat
numberrequis
Latitude, degrés nord.
Autorisé -90–90
└lon
numberrequis
Longitude, degrés est.
Autorisé -180–180
└height
numberfacultatif
Mètres au-dessus du niveau de la mer.
Autorisé -500–10000
layout_width
integerfacultatif
L'annotation est composée comme pour la photo affichée avec cette largeur en pixels, puis mise à son échelle : les épaisseurs de trait et tailles de police sont des pixels de cette vue. Plus grand : des étiquettes plus petites et plus nombreuses.
Par défaut 1400Autorisé 200–8000
detail
numberfacultatif
Combien montrer de plus, comme le zoom le fait dans l'atelier : 0 la sélection par défaut (objets marquants), 1 tout objet que le filtre laisse passer.
Par défaut 0Autorisé 0–1
view
objectfacultatif
Calques et styles : voir Options.
filter
objectfacultatif
Quels groupes et magnitudes s'affichent : voir Options.
edits
objectfacultatif
Objets masqués ou renommés un à un : voir Options.
requête
{
  "solution": { "width": 2048, "height": 1366, "wcs": { … }, … },
  "format": "svg",
  "time": "2025-08-16T23:00:00+02:00",
  "location": { "lat": 52.23, "lon": 21.01 },
  "view": { "group_by": "catalog", "grid": { "on": false }, "catalogs": { "solar": { "color": "#ff8800", "size": 16 } } },
  "filter": { "catalogs": { "ldn": false, "lbn": false }, "magnitude": [-2, 10] },
  "edits": { "hidden": ["Mars"], "names": { "NGC 1499": "California" } }
}

Avec format: "svg" la réponse est en image/svg+xml : <svg width="2048" height="1366" viewBox="0 0 2048 1366">…, dans les pixels de la photo. Un avertissement sur le modèle de ciel, s'il y en a un, vient dans l'en-tête SkySolver-Warning.

Modèles de ciel

Envoyez celui que vous avez ; tous décrivent comment les pixels de l'image se projettent sur le ciel.

l'un de
corners
object
Le ciel au centre de l'image et à ses quatre coins extérieurs, chacun {ra, dec} : center, top_left, top_right, bottom_right, bottom_left. Une projection gnomonique (TAN) leur est ajustée ; pour les objectifs grand-angle qui distordent, la réponse avertit de l'écart des coins. Chaque coin doit être à moins de 87° du centre : les vues plus larges exigent la projection du solveur.
wcs
object
TAN ou TAN-SIP tel que le solveur le donne : crval [RA, Dec], crpix [x, y] (en base 0), cd [[CD1_1, CD1_2], [CD2_1, CD2_2]] en degrés par pixel, et en option sip {order, a, b, inv_order, ap, bp} avec des termes [p, q, coefficient].
fits_header
string
Un en-tête FITS WCS en texte (cartes de 80 caractères, ou une carte par ligne) : CTYPE1 = 'RA---TAN' ou 'RA---TAN-SIP', CRVAL, CRPIX (en base 1, comme FITS), et CD, ou PC avec CDELT, ou CDELT avec CROTA2 ; les A_*, B_*, AP_*, BP_* de SIP. Ce qu'écrivent astrometry.net, ASTAP, PixInsight et ce solveur.
projection
object
Le modèle de projection du solveur (panoramas, fisheye et images de tout le ciel), tel que /solve le renvoie.
solution
object
Une réponse /solve entière : sa taille et son wcs ou sa projection sont utilisés.

Sortie JSON

Avec format: "json" : ce que l'annotation dessine, en pixels de l'image (base 0, y vers le bas), et les étiquettes telles que placées.

champs
width, height
integer
La taille d'image pour laquelle l'annotation a été dessinée.
field
object
center {ra, dec}, scale_arcsec (au centre), diagonal_deg (null pour les vues de tout le ciel).
layout
object
width (la layout_width utilisée) et scale : pixels de l'image par pixel de composition, par lequel tailles et épaisseurs ont été multipliées.
objects
array
Les objets du ciel profond que le filtre et la vue sélectionnent, masqués compris (shown: false).
└id, label
string
Désignation (M 42, NGC 1977), telle que edits la prend, et l'étiquette dessinée (M42).
└name, catalogue_name
string | null
Le nom dessiné (Orion Nebula, ou celui donné dans edits) et celui du catalogue.
└type, group, catalogs
string, string, array
Type lisible, son groupe (emission…) et chaque groupe de catalogue dont il fait partie.
└other_designations
array
Ses autres désignations : ["Mel 22", "LBN 770"].
└ra, dec, mag
number
Position et magnitude (null quand inconnue).
└size_arcmin, position_angle
array, number
[grand axe, petit axe] et l'angle du nord vers l'est, quand ils sont connus.
└x, y, radius_px
number
Où il est dans l'image, et son rayon en pixels.
└outline, closed
array | null, boolean
Sa forme en anneaux [x0, y0, x1, y1, …] en pixels (un contour tracé, ou l'ellipse du catalogue) ; closed est false quand la forme sort du domaine d'une vue de tout le ciel et que seuls des morceaux en sont dessinés.
└shown
boolean
false quand edits.hidden le masque.
bodies
array
Avec time : le Soleil, la Lune, les planètes, astéroïdes et comètes dans le cadre : id, name, kind (star, moon, planet, dwarf planet, asteroid, comet), ra, dec, mag (pour une comète, une prévision grossière), diameter_arcsec (null pour les astéroïdes et comètes), distance_au, phase (fraction éclairée), elongation_deg, topocentric (placé comme vu depuis location), uncertainty_arcsec (astéroïdes et comètes : de combien la position peut être décalée), x, y, radius_px, outline, shown.
constellations
array
id, name, lines (polylignes en pixels), stars, label_at.
grid
array
Lignes de RA et de Dec : kind (ra ou dec), value (degrés), label, lines.
lines
array
Les lignes de référence activées : kind (ecliptic, galactic, borders), lines, label_at.
labels
array
Chaque étiquette telle que placée, sans chevaucher les autres : text, of (l'objet), kind, x, y (l'ancre de la ligne de base du texte), size, anchor (start, middle, end), color, opacity.
warnings, time, location
array, string, object
Avertissements sur le modèle de ciel ; l'heure (UTC) et le lieu utilisés.

Options

Toutes facultatives, et partielles : ce que vous omettez garde les valeurs par défaut de l'atelier. Les valeurs hors plage, et les clés qui n'existent pas, sont refusées avec 422 plutôt qu'ignorées.

view
group_by
stringfacultatif
Les objets sont stylés et filtrés par type ou par catalogue : types ou catalogs ci-dessous s'appliquent alors.
Par défaut "type"Autorisé type · catalog
grid
objectfacultatif
La grille RA/Dec : on et un style de trait (par défaut #96beff, en tirets).
constellations
objectfacultatif
Figures et noms : on et un style de trait.
borders
objectfacultatif
Les limites des constellations de l'UAI (Delporte 1930, précessées à J2000) : on (désactivé par défaut) et un style de trait.
ecliptic, galactic
objectfacultatif
L'écliptique et l'équateur galactique, avec leurs noms : on (désactivé par défaut) et un style de trait.
shadow
objectfacultatif
Une ombre portée sous toute l'annotation : on (désactivée par défaut), color, opacity, blur (0–16 px), distance (0–12 px).
objects
booleanfacultatif
Dessiner les objets (et le Soleil, la Lune, les planètes, astéroïdes et comètes).
Par défaut true
labels
booleanfacultatif
Dessiner les étiquettes.
Par défaut true
types
objectfacultatif
Un style de trait par groupe de types, avec les clés de Groupes et styles : {"emission": {"color": "#ff99cc"}}.
catalogs
objectfacultatif
Un style de trait par groupe de catalogues.
un style de trait
color
stringfacultatif
Couleur des traits, marqueurs et étiquettes.
Autorisé #rrggbb
opacity
numberfacultatif
Opacité des traits et marqueurs ; les étiquettes sont dessinées un peu plus opaques.
Autorisé 0–1
width
numberfacultatif
Épaisseur de trait, en pixels de la vue de composition (voir layout_width).
Autorisé 0.3–6
dash
stringfacultatif
Style de trait.
Autorisé solid · dashed · dotted · dashdot
size
numberfacultatif
Taille de police des étiquettes, en pixels de la vue de composition.
Autorisé 6–32
filter
types
objectfacultatif
Groupes activés ou désactivés (avec group_by: "type") : {"dark": false}. Tous sont activés par défaut.
catalogs
objectfacultatif
La même chose pour les catalogues (avec group_by: "catalog").
magnitude
string | arrayfacultatif
auto : les objets Messier toujours, les autres selon leur importance dans le cadre (taille, ou une limite de magnitude qui suit l'échelle de l'image). Une plage : les objets dont la magnitude est dedans ; ceux qui n'en ont pas (la plupart des nébuleuses) selon leur taille.
Par défaut "auto"Autorisé "auto" · [brightest, faintest] within -2–18
edits
hidden
arrayfacultatif
Objets à omettre, par désignation telle que objects[].id la donne : ["NGC 1999", "Mars"].
names
objectfacultatif
Noms à donner aux objets : {"NGC 1499": "California"} ; une chaîne vide étiquette un objet par sa seule désignation.

Groupes et styles

Les clés de view.types, filter.types, view.catalogs et filter.catalogs, avec leur style par défaut (couleur, opacité, épaisseur, trait, taille de police) :

clé typesGroupePar défaut
galaxyGalaxies#f4b8700.75 · 1 px · solid · 10.5 px
openAmas ouverts#86d3f30.75 · 1 px · solid · 10.5 px
globularAmas globulaires#f3dc8e0.78 · 1 px · solid · 10.5 px
emissionNébuleuses en émission#d4a5ff0.78 · 1 px · solid · 10.5 px
reflectionNébuleuses par réflexion#9cc3ff0.75 · 1 px · solid · 10.5 px
planetaryNébuleuses planétaires#7ee8c60.78 · 1 px · solid · 10.5 px
snrRémanents de supernova#ff9eb40.75 · 1 px · solid · 10.5 px
darkNébuleuses obscures#b5c0d40.45 · 1 px · solid · 10.5 px
starÉtoiles#fde9b00.7 · 0.9 px · solid · 10 px
solarSystème solaire#e4f68e0.85 · 1.2 px · solid · 12 px
otherAutres#dce3f00.65 · 1 px · solid · 10.5 px
clé catalogsGroupePar défaut
messierMessier#f7cf4a0.85 · 1 px · solid · 12 px
ngcNGC#86d3f30.75 · 1 px · solid · 10.5 px
icIC#aab8ff0.75 · 1 px · solid · 10.5 px
sharplessSharpless (Sh2)#d4a5ff0.78 · 1 px · solid · 10.5 px
barnardBarnard (B)#b5c0d40.5 · 1 px · solid · 10.5 px
ldnLynds obscures (LDN)#93a1b80.45 · 1 px · solid · 10.5 px
lbnLynds brillantes (LBN)#f7a8d30.72 · 1 px · solid · 10.5 px
vdbvan den Bergh (vdB, vdBH)#9cc3ff0.75 · 1 px · solid · 10.5 px
caldwellCaldwell#7ee8c60.75 · 1 px · solid · 10.5 px
clustersAmas d'étoiles (Collinder, Melotte, Trumpler, …)#a5e4f70.72 · 1 px · solid · 10.5 px
pnNébuleuses planétaires (PN G, PK, Abell)#7ee8c60.78 · 1 px · solid · 10.5 px
snrRémanents de supernova (SNR G)#ff9eb40.75 · 1 px · solid · 10.5 px
groupsGroupes de galaxies (Arp, Hickson, Abell/ACO)#f4c48a0.7 · 1 px · solid · 10.5 px
rcwAutres nébuleuses (RCW, Gum, Cederblad, DWB)#e0b3ff0.72 · 1 px · solid · 10.5 px
starsÉtoiles (noms, Bayer, Flamsteed, HR, HD)#fde9b00.7 · 0.9 px · solid · 10 px
customVos catalogues (fichiers)#ffb86b0.85 · 1 px · solid · 11 px
vizierEn ligne (VizieR)#9fe8700.7 · 0.9 px · solid · 9.5 px
solarSystème solaire#e4f68e0.85 · 1.2 px · solid · 12 px
otherAutres#dce3f00.65 · 1 px · solid · 10.5 px

Erreurs

Chaque réponse d'erreur a la même forme : un code sur lequel agir, et un message à afficher. Les requêtes invalides listent chaque problème :

422
{
  "error": "invalid_request",
  "message": "view.grid.color: String should match pattern '^#[0-9a-fA-F]{6}$'",
  "details": [
    { "field": "view.grid.color", "problem": "String should match pattern '^#[0-9a-fA-F]{6}$'" },
    { "field": "filter.magnitude", "problem": "magnitude must be \"auto\" or [brightest, faintest] within -2..18" }
  ]
}
StatuterrorQuand
400invalid_imageL'image n'est pas du base64 valide, ou ne peut pas être lue.
400unsupported_formatLe fichier n'est pas dans l'un des formats ci-dessus.
413payload_too_largeLe corps de la requête, le fichier ou son nombre de pixels dépasse la limite.
422invalid_requestUn champ manque, est du mauvais type, hors plage ou inconnu ; details les liste un à un.
429rate_limitedVotre adresse cliente a épuisé ses requêtes ; retry_after dit quand réessayer.
429busyLe serveur résout autant qu'il peut ; réessayez après Retry-After.
500internal_errorQuelque chose a échoué de notre côté.
503unavailableLe solveur, le solveur lunaire ou l'annotateur n'est pas prêt.

Une image qui ne peut pas être résolue n'est pas une erreur : la réponse est 200 avec solved: false.

État du service

GET/api/v1/health60 d'un coup · 60 par minute

L'état du service, les index d'étoiles, le solveur lunaire et les workers d'annotation chargés, et les limites en vigueur.

réponse
{
  "status": "ok",
  "version": "1.0",
  "uptime_s": 86400.2,
  "solver": { "indexes": 4, "max_concurrent": 4, "in_flight": 0 },
  "moon": { "ready": true },
  "annotator": { "ok": true, "workers": 2, "objects": 15333 },
  "limits": {
    "rates": {
      "image": { "burst": 10, "per_minute": 1 },
      "moon": { "burst": 20, "per_minute": 10 },
      "stars": { "burst": 30, "per_minute": 30 },
      "annotate": { "burst": 30, "per_minute": 30 },
      "light": { "burst": 60, "per_minute": 60 }
    },
    "image_mb": 20, "megapixels": 120, "stars": 10000, "time_limit_ms": 30000
  }
}

API compatible Astrometry.net

Les programmes qui résolvent en ligne avec astrometry.net (nova.astrometry.net) peuvent utiliser SkySolver à la place : réglez leur serveur sur https://skysolver.app. N'importe quelle clé d'API fonctionne, une clé vide aussi ; il n'y a pas de comptes. Les routes, les réponses et les fichiers sont ceux de nova, jusqu'aux détails sur lesquels ses clients comptent, à la racine du site (/api/…, /wcs_file/…).

ProgrammeRéglage
N.I.N.A.Options → Plate solving → Astrometry.net : URL https://skysolver.app, n'importe quelle clé d'API
KStars / EkosAlign → solver options → Online (astrometry.net) : API URL https://skysolver.app (https : StellarSolver l'ajoute de toute façon), n'importe quelle clé
astroqueryAstrometryNet() avec URL = "https://skysolver.app", API_URL = "https://skysolver.app/api", n'importe quel api_key
client.py (astrometry.net)--server https://skysolver.app/api/ --apikey anything ; le script en ligne de CCDciel l'utilise
AstroImageJPlate solve → custom server https://skysolver.app, clé vide
SGPle solveur ANSVR, Server URL https://skysolver.app/api (les routes que SGP appelle y sont ; non essayé avec SGP lui-même)
astroquery
from astroquery.astrometry_net import AstrometryNet

ast = AstrometryNet()
ast.URL, ast.API_URL, ast.api_key = "https://skysolver.app", "https://skysolver.app/api", "anything"
wcs_header = ast.solve_from_image("m31.jpg")            # or ast.solve_from_source_list(x, y, width, height)
RouteCe qu'elle fait ici
POST /api/loginUne session pour n'importe quelle clé.
POST /api/uploadUne image (JPEG, PNG, TIFF, FITS, XISF, BMP, GIF, WebP), une liste x,y (une table FITS avec X et Y, ou du texte avec un x y par ligne), ou des listes x et y dans request-json. Jusqu'à 96 MB.
POST /api/url_uploadListes x et y seulement (comme astroquery les envoie) : aucune URL n'est téléchargée.
/api/submissions/IDLa soumission et son job : le job est là dès la première interrogation.
/api/jobs/IDprocessing, solving, success ou failure ; chaque job finit dans l'un des deux derniers.
/api/jobs/ID/calibration/ra, dec, radius, pixscale, orientation, parity, width_arcsec, height_arcsec, calculés comme nova le fait.
/api/jobs/ID/info/ · tags/ · machine_tags/ · objects_in_field/ · annotations/Les objets du ciel profond dans le champ, avec leurs positions et rayons en pixels dans annotations.
/wcs_file/IDLa solution TAN-SIP en fichier FITS d'en-tête seul (wcs.fits).
/corr_file/ID · /axy_file/ID · /rdls_file/IDLes étoiles appariées, les étoiles détectées, les étoiles du catalogue appariées : des tables FITS dans les colonnes de nova.
/joblog/IDCe que le job a fait, en texte.
  • Toujours à l'aveugle. Les indications d'échelle, de position et de parité sont acceptées et ignorées : le solveur n'en a pas besoin, et une indication fausse ne peut pas gâcher une résolution.
  • Les pixels sont ceux de nova : (1, 1) est le centre du premier pixel stocké, le coin supérieur gauche d'un JPEG ou PNG (son orientation EXIF non appliquée) ou la première ligne d'un fichier FITS, quel que soit son ROWORDER. L'orientation garde la particularité de nova pour les envois JPEG, PNG et GIF, si bien que les programmes qui la retournent obtiennent le même angle qu'avec nova.
  • Les panoramas et images de tout le ciel résolus avec un modèle de projection ont une calibration mais pas de wcs_file : aucun TAN-SIP ne les contient.
  • Aucune image n'est conservée. Un envoi est abandonné dès que ses étoiles sont trouvées, si bien que new_fits_file et les images annotée, rouge-verte et d'extraction ne sont pas proposées. Les résultats d'un job sont gardés en mémoire 30 minutes, puis oubliés.
  • Limites : envois 20 d'un coup et 6 par minute par adresse ; les autres routes comptent comme requêtes légères (60 d'un coup, 60 par minute).

Journal des modifications

  • 1.2 (19 octobre 2026) : résolution de la Lune : /api/v1/solve/moon.
  • 1.1 (17 octobre 2026) : l'API compatible Astrometry.net.
  • 1.0 (16 octobre 2026) : résoudre une image (base64) ou des positions d'étoiles, annoter en SVG ou JSON, avec le Soleil, la Lune et les planètes.

Des questions, un bug, un quota plus large ? [email protected]. En utilisant l'API vous acceptez les conditions d'utilisation ; ce qu'elle fait de vos données est dans la page confidentialité.