Add cards with the API
List your decks and add up to 100 cards at a time, including custom fields, with a single request.
Adding cards is what most scripts are for. It takes two steps: find the deck, then post the cards to it.
List your decks
Needs a key with the decks:read scope.
curl https://yalango.com/api/v1/decks \
-H "Authorization: Bearer yal_your_key_here"
Decks come back newest first, 50 at a time. Use ?limit= to change the page size, up to 200.
{
"decks": [
{
"doc_id": "8sKd92mfPqR1xLvBn4Tz",
"id": 48210937465,
"name": "Spanish verbs",
"description": "",
"source_ISO_639-1": "en",
"target_ISO_639-1": "es",
"privacy": "private",
"number_of_items": 214,
"tags": "",
"created_timestamp": "2026-01-14T09:22:31.000Z",
"last_updated_timestamp": "2026-03-02T18:05:12.000Z"
}
],
"next_cursor": "eyJ0cyI6MTc0MDkzMDMxMjAwMCwiZG9jX2lkIjoiOHNLZDkybWZQcVIxeEx2Qm40VHoifQ"
}
When next_cursor is not null there are more decks. Pass it back to get the following page, and keep going until it comes back null:
curl "https://yalango.com/api/v1/decks?cursor=eyJ0cyI6MTc0..." \
-H "Authorization: Bearer yal_your_key_here"
Treat the cursor as opaque - it encodes both a timestamp and a deck id, and its format may change.
Add cards
Needs a key with the cards:write scope. The doc_id in the URL is the one from the list above.
curl -X POST https://yalango.com/api/v1/decks/8sKd92mfPqR1xLvBn4Tz/cards \
-H "Authorization: Bearer yal_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"cards": [
{ "source": "the dog", "target": "el perro" },
{ "source": "the cat", "target": "el gato" }
]
}'
{
"created": 2,
"card_ids": ["Lp4nVx8QmRt2YsKd", "Bq7WcZ3jHf5TnXvA"]
}
source is the side in the language you know, target the side in the language you are learning. The card's languages, privacy and author all come from the deck, so you do not set them.
Rules for a batch
- Between 1 and 100 cards per request.
sourceandtargetare each up to 2000 characters, and at least one of the two must be non-empty.- The whole request body must be under 256 KB.
One bad card rejects the whole batch
If any card fails validation, nothing is written and no quota is used. The error names the exact card and field:
{
"error": {
"code": "invalid_card",
"message": "cards[3].target must be at most 2000 characters.",
"param": "cards[3].target",
"request_id": "req_8fKd2mXp4LvNc7Bh"
}
}
This is deliberate. The alternative - writing the good cards and reporting the rest - means every retry risks duplicating whatever succeeded the first time, and every caller has to write reconciliation logic. Rejecting the batch means your state is always knowable and a retry is always safe.
Custom fields
If your deck has custom fields, you can fill them in at the same time. The keys are the field ids, not their display names:
curl -X POST https://yalango.com/api/v1/decks/8sKd92mfPqR1xLvBn4Tz/cards \
-H "Authorization: Bearer yal_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"cards": [
{
"source": "the dog",
"target": "el perro",
"custom_fields": { "kL9mQ2xVnB4t": "masculine" }
}
]
}'
You can find a field's id in the deck's custom field settings. The API only accepts fields that already exist on the deck - creating a new one through a card write would produce data the web app has no way to display. Create the field in the web app first.
Retrying safely
Like deck creation, this endpoint accepts an Idempotency-Key header:
curl -X POST https://yalango.com/api/v1/decks/8sKd92mfPqR1xLvBn4Tz/cards \
-H "Authorization: Bearer yal_your_key_here" \
-H "Idempotency-Key: chapter-4-vocab" \
-H "Content-Type: application/json" \
-d '{"cards": [{"source": "the dog", "target": "el perro"}]}'
This matters more here than anywhere else: without it, a timeout on a 100-card batch leaves you unable to tell whether the cards landed, and retrying blind could add all 100 twice. With it, the retry returns the original response.
Use a new key for each genuinely new batch. Keys are remembered for 24 hours.
What happens next
Cards added through the API are indistinguishable from cards typed into the web app. They join your vocabulary, count toward your review schedule, and appear in games straight away.
On Premium, audio is generated automatically in the background, exactly as it is for an import from the web app.
Card limits
The free plan allows 10,000 cards. A batch that would take you over is rejected with 402 quota_exceeded, and the message says how many you have and how many you can still add. Nothing is written.
If you have a large one-off list, the web app's import tools handle a file in one go and may be less work than scripting it.