๐Ÿ› ๏ธ

The LeelaMaps API

Post and manage your own notes, events and lists from a script

โ† Back to map | Posting Guidelines | Terms of Service | Credits
No code? A spreadsheet does the same: leelamaps.com/import has a template and the importer.

Contents

  1. Getting a key
  2. Basics
  3. Endpoints
  4. Note fields
  5. Where a note goes
  6. Events
  7. Photos, links and prices
  8. Re-running a batch safely
  9. Many notes at once
  10. Drafts
  11. Possible duplicates
  12. Limits and house rules

1. Getting a key

Open the map, then your profile โ†’ Edit profile โ†’ API keys โ†’ New key. Name it after what will use it ("seeding script"). The key is shown once: copy it somewhere safe. You can have up to 10 keys, and revoke any of them from the same screen.

A key acts as you, on your own notes only. It can't read anyone else's private notes, and it can't do anything an admin can.

2. Basics

Send the key in an Authorization header, and JSON bodies with Content-Type: application/json. Check that a key works:

curl https://leelamaps.com/api/v1/me \
  -H "Authorization: Bearer YOUR_KEY"

Errors come back as {"error": "โ€ฆ", "field": "โ€ฆ"} with a 4xx status: 400 (a field is wrong โ€” field names it), 401 (no key, or a revoked one), 403, 404 (no such note of yours), 409, 422 (a place we couldn't find for sure โ€” reason says why, see Where a note goes), 429 (over a limit), 503 (the place lookup is down โ€” try again).

3. Endpoints

RequestDoes
GET/api/v1/meWhose key this is.
POST/api/v1/notesCreate a note โ€” or update yours with the same external_id. Returns 201 with "created": true, or 200 with "updated": true.
GET/api/v1/notesYour notes, newest first. Filters: list_id, external_id. Paging: limit (max 100), then cursor = the next_cursor you were given.
GET/api/v1/notes/<id>One of your notes.
PATCH/api/v1/notes/<id>Change or move a note. Only the fields you send change.
DELETE/api/v1/notes/<id>Delete a note.
POST/api/v1/notes/<id>/publishPublish a draft now.
GET/api/v1/listsYour lists.
POST/api/v1/listsMake a list: {"name", "description"}.
PATCH/api/v1/lists/<id>Rename a list or change its description.

A whole example โ€” a free seed library, filed into a list:

curl https://leelamaps.com/api/v1/notes \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "ACLD Seed Library",
    "description": "Free seeds, no card needed. Take a few, bring some back.",
    "emoji": "๐ŸŒฑ",
    "address": "401 E University Ave, Gainesville, FL",
    "tags": ["free", "seeds"],
    "list_ids": [123],
    "external_id": "acld-seed-library"
  }'

Move it later:

curl -X PATCH https://leelamaps.com/api/v1/notes/4567 \
  -H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" \
  -d '{"lat": 29.6519, "lng": -82.3226}'

4. Note fields

FieldMeaning
title, descriptionThe note's first line, and the words under it. One of the two is required to create.
lat, lng or addressWhere it is: coordinates, or a street address or venue name we look up. See Where a note goes.
nearWhere your places are, roughly: {"lat": โ€ฆ, "lng": โ€ฆ} or a town ("Gainesville, FL"). Lets a venue name be found, and refuses any match more than 150 km from it.
emojiThe pin's emoji. Default ๐Ÿ“.
marker_colorThe pin's colour: red, orange, yellow, green, teal, blue, purple, pink, dark, or #rrggbb. Empty or left out: colour by privacy, as in the app.
marker_styleThe pin's shape: circle, glow, ring, diamond, dot or pin.
privacypublic (default), friends, friends_of_friends or private.
locals_only, locals_radius_kmOnly here: only people who are, or were recently, within the radius (default 5 km) can open it.
hide_locationNo location: no pin. It reaches people near where it was written, and your friends, as a card; nobody but you is told the exact spot.
draft, publish_atSee Drafts.
tagsA list of words (up to 15). Sending tags on a PATCH replaces them.
list_idsLists of yours to file it into.
eventsSee Events.
itemsPhotos and links in the note's library. See Photos, links and prices.
archive_afterA date it leaves the map (it stays in your archive), or "never". Empty: an event's note archives after its last day; a plain note stays. See Timing and status.
repeat_everyweekly, monthly or yearly: it reposts on that cycle. Empty to stop.
seasonWhen it is in season: "Mar 15 - Jun 30", "June-August", "summer" (or season_start/season_end as MM-DD); season_off dim (default) or hide. Empty to clear.
statusA live status on the pin: "๐ŸŸข Open now", or {"emoji", "label", "color", "for": "2 hours"} (a week at most; left out = until cleared). null clears it.
media_contribWho besides you may add items to its library: anyone (the default), friends, locals (people there), or off.
media_contrib_reviewtrue (the default): a reader's item waits for your OK; false: it shows straight away.
external_idYour own id for it (up to 120 characters). See Re-running a batch.

5. Where a note goes

Send lat and lng and that is where it goes. Otherwise we look up address โ€” or, with no address, the first event's venue โ€” and only accept a match we're sure of. We never guess: a venue name alone matches streets of the same name on other continents, so it is refused rather than pinned there.

  • A full street address ("1309 NW 192nd Ave, Gainesville, FL") is found anywhere.
  • A venue name is found when you say where it is โ€” in the text ("Bo Diddley Plaza, Gainesville FL") or with near โ€” and the place we find is called what you asked for.
  • The answer says how: "placed": {"by": "address" | "venue", "label": "โ€ฆ"} (null for coordinates).

When we can't be sure you get a 422 with a reason:

reasonMeaning
needs_nearA venue name and nothing else. Add its town, or send near.
too_vagueOnly a town or region matched. Add the street address.
far_from_hintThe best match is more than 150 km from near.
not_foundNothing matched. Send coordinates.
curl https://leelamaps.com/api/v1/notes \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"title": "Jazz on the Green", "near": "Gainesville, FL",
       "events": [{"date": "2026-10-10", "start_time": "7pm", "venue": "Celebration Pointe Promenade"}]}'

6. Events

events is a list; each one becomes an Event in the note's library, exactly as one made in the app โ€” with RSVP, a place in people's day plans, and the note archiving itself after the date.

"events": [{
  "title": "Pumpkin Bash",
  "date": "2026-10-25",
  "start_time": "4pm",
  "end_time": "8:30 pm",
  "venue": "Depot Park",
  "rsvp": true,
  "capacity": 200
}]
  • date is required. 2026-10-25, 10/25/2026 and Oct 25, 2026 all work. Also: end_date, description, capacity, recurrence (weekly or monthly).
  • Times: 19:00, 7pm, 7:30 pm, noon. A bare 7 is refused โ€” morning or evening?
  • On a PATCH, events replaces the note's events. Leave it out to keep them; send [] to remove them.
  • What you read back is the same shape, so you can edit it and send it again.

ticket_price: a number (15), or "$10-25" (a sliding scale) or "pay what you can"; 0/"free" for none. People request a ticket and you arrange payment; card payment for an event is turned on in the app. Anything unreadable is a 400.

recurrence takes weekly (on the event's own weekday), monthly, or days: "weekly: Tue, Thu" / "every Saturday" โ€” or "recurrence": "weekly", "recurrence_days": [1, 3] (0 = Monday). Anything else is a 400.

Timing and status

{"title": "Farm stand", "lat": 29.65, "lng": -82.32,
 "publish_at": "2027-03-01T08:00:00-05:00", "season": "Mar 1 - Oct 31", "season_off": "dim",
 "repeat_every": "weekly", "archive_after": "never",
 "status": {"emoji": "๐ŸŸข", "label": "Open now", "color": "green", "for": "8 hours"}}

Anything unreadable is a 400 naming the field; nothing is guessed.

7. Photos, links and prices

items is a list of up to 10 (at most 5 photos), each becoming an item in the note's library, as if added in the app:

"items": [
  {"type": "image", "url": "https://example.org/stage.jpg", "title": "The stage",
   "price": 12, "stock": 20},
  {"type": "link",  "url": "https://example.org/tickets", "title": "Tickets"}
]
  • Photos are copied into LeelaMaps when you send them โ€” never linked to your server โ€” so the URL only has to work once. It must be a public http(s) address of an image under 10 MB.
  • A link gets a card from its page's title and description.
  • price (dollars) and stock are optional; a priced item shows its price on the card.
  • On a PATCH, items replaces the items the API added before; ones added in the app stay. Leave it out to keep them.
  • If any item can't be added (reason: image_failed, blocked_url, bad_price, too_many_imagesโ€ฆ) the whole note is refused and nothing is saved, so a retry never leaves half a note.

8. Re-running a batch safely

Give every note an external_id โ€” the row number in your spreadsheet, the event's id on its own site. POSTing the same external_id again updates that note instead of making a second one, so a script can be run again after fixing a typo, or every week as a calendar changes. GET /api/v1/notes?external_id=โ€ฆ finds it.

9. Many notes at once

POST /api/v1/notes/batch takes up to 50 notes, each exactly as POST /api/v1/notes would, and answers for each one. One bad note never stops the others. A near on the batch applies to every note that gives none.

curl https://leelamaps.com/api/v1/notes/batch \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"near": "Gainesville, FL", "notes": [
        {"external_id": "ev-101", "title": "Swing night", "events": [{"date": "2026-10-15", "start_time": "8pm", "venue": "Rosa B. Williams Center"}]},
        {"external_id": "ev-102", "title": "Open mic", "address": "4908 NW 34th Blvd, Gainesville, FL"}]}'
{"created": 2, "updated": 0, "error": 0, "not_attempted": 0,
 "results": [{"index": 0, "status": "created", "id": 4567, "external_id": "ev-101", "placed": {"by": "venue", โ€ฆ}, "duplicates": []}, โ€ฆ]}

status is created, updated (that external_id was already yours), error (with error, field, reason), or not_attempted: a batch works for about 45 seconds, and notes it didn't reach are untouched. Send those again โ€” with external_ids, re-sending is always safe. Each note counts toward the hourly limits.

10. Drafts

Send "draft": true and the note is saved but seen by nobody else until you publish it โ€” from the app, or with POST /api/v1/notes/<id>/publish. Or send publish_at (an ISO date-time, e.g. 2026-10-20T09:00:00-04:00) and it goes live by itself then.

11. Possible duplicates

Every create and update answers with duplicates: notes within about 100 m whose title looks like yours ([{"note_id", "title", "distance_m", "mine"}]). It's a warning, not a refusal โ€” look before you post the same place twice.

12. Limits and house rules

  • Per key, per hour: 500 new notes, 1,500 writes in all, 3,000 reads, 300 photos copied in. Over a limit you get a 429 and nothing is saved.
  • Notes made through the API never earn Loops, and posting one sends no notifications โ€” they appear on the map for whoever looks. (A note you publish from a draft announces itself like any other, still without Loops.)
  • Everything you post follows the Posting Guidelines and the Basic Humanity Contract, which your account must have agreed to in the app.
Questions, or need more than the limits allow? Send feedback from the map's menu and say it's about the API.

๐Ÿ“ LeelaMaps  ยท  Terms of Service  ยท  Privacy Policy  ยท  Posting Guidelines  ยท  Import places  ยท  Credits