FlashVocab is a self-hosted spaced repetition app for vocabulary learning, powered by the FSRS algorithm — the most accurate open-source scheduling algorithm available today. Here's what you can do:
| Category | Features |
|---|---|
| Spaced Repetition | FSRS algorithm, 4-grade rating system (Again/Hard/Good/Easy), keyboard shortcuts, review filters by tag/state/mode, progress bar, session timer, interval previews |
| Deck Management | Create language decks with source/target language config, edit, duplicate (resets FSRS state), delete with cascade, share with other users (read-only) |
| Word Management | Add single or batch, import via paste JSON or file upload, search & filter by tag/state, edit, delete, duplicate detection, FSRS details per word |
| Import & Export | JSON import/export per deck, FSRS state preservation, tag-filtered export, import modal on Words & Decks pages, tags input on import, SuperMemo migration |
| Analytics | 30-day retention rate, daily review chart, 7-day due forecast, session timer & history, study time totals, cards/session metrics, total reviews |
| Multi-User | Role-based access (user, api_user, admin), admin panel for user CRUD, deck sharing by email (read-only), account lockout after 3 failed attempts |
| API & AI Integration | 30+ REST endpoints, API key auth for scripts/AI, machine-readable /api/spec, one-click key copy, batch API for AI word generation |
| Security | JWT with httpOnly cookies, API keys (SHA256 hashed), bcrypt passwords, 8-char min length, account lockout, role-based permissions |
After logging in, you'll be redirected to the Decks page. Select or create a deck, then start adding words and reviewing.
The Decks page is your home screen. It shows all your decks in a grid layout with word counts, due badges, and action buttons.
| Action | Description |
|---|---|
| New Deck | Create a deck with a name, description, and source/target language pair (e.g., Spanish → Polish) |
| Select & Study | Sets the deck as active and redirects to the Review page. The active deck name appears in the nav bar |
| Import | Opens the import modal to paste JSON or upload a file directly into that deck |
| Share | Share the deck with another user by email. They get read-only access |
| Edit | Change the deck name, description, or language settings |
| Duplicate | Creates a copy of the deck with all words. FSRS scheduling state is reset (all words start as New) |
| Delete | Permanently removes the deck and all its words. This cannot be undone |
The nav bar shows a deck selector dropdown — you can switch between decks from any page without going back to the Decks page. All pages (Review, Words, Stats) are scoped to the active deck.
Due badge: Each deck card shows how many cards are due for review. An orange badge appears when cards are due, helping you prioritize which decks to study.
Navigate to the Words page to manage your vocabulary. You can add words one at a time or in batch via the API.
Each word can include:
Duplicate detection: If you add a word that already exists in the deck (case-insensitive match), you'll see a warning with the option to "Add Anyway" or cancel.
Search and filter: Use the search box to find words by text, the tag dropdown to filter by tag, and the state dropdown to show only New, Learning, Review, or Relearning cards.
FlashVocab provides an Import button on both the Words page and each deck card on the Decks page. Click it to open the import modal.
The import modal has two tabs:
.json file from your computerThe import modal performs strict field validation. Only the following field names are accepted — any unknown fields will be rejected with an error showing the allowed fields:
| Field | Required | Description |
|---|---|---|
word | Yes | The vocabulary word (e.g., "hola") |
translation | Yes | The translation (e.g., "czesc") |
example_sentence | No | An example sentence using the word |
example_translation | No | Translation of the example sentence |
tags | No | Comma-separated tags (e.g., "travel,A2") |
Example JSON:
[
{
"word": "Disparar",
"translation": "To skyrocket",
"example_sentence": "Lidl dispara su beneficio en España.",
"example_translation": "Lidl's profit is skyrocketing in Spain.",
"tags": "business,B2"
}
]
Common mistakes: Using definition instead of translation, context instead of example_sentence, or camelCase names like exampleSentence. The validation will catch these and show you the correct field names.
Both tabs accept two JSON formats:
[{ "word": "...", "translation": "..." }]{ "words": [{ "word": "...", "translation": "..." }] }Tags on import: Enter comma-separated tags in the "Tags to apply" field. These tags are applied to any imported words that don't already have tags set in the JSON. Words that already have tags in the JSON keep their original tags.
Duplicate handling: After importing, you'll see:
Typical use cases:
The Review page is the core of the app. Cards appear one at a time: you see the word, reveal the answer, then grade yourself.
Space or click the card to reveal the answer (translation + example).1-4).| Grade | Key | Meaning | Effect on scheduling |
|---|---|---|---|
| Again | 1 | Forgot the word | Reset to short interval, increases lapse count |
| Hard | 2 | Recalled with difficulty | Shorter next interval than normal |
| Good | 3 | Recalled correctly | Normal interval increase |
| Easy | 4 | Recalled effortlessly | Larger interval increase |
Review filters: Before starting, you can filter by tags, state (New/Learning/Review), or mode (Due only vs. all cards).
The Stats page shows comprehensive learning analytics for the active deck.
| Metric | What it tells you |
|---|---|
| Total Words | Total vocabulary in the active deck |
| Due Now | Cards overdue for review — this is the number you see on each deck's badge |
| Reviewed Today | How many cards you've reviewed in today's sessions |
| Retention (30d) | Percentage of reviews where you answered correctly (not "Again") over the last 30 days. This is your memory accuracy |
| New / Learning / Review | Card counts by FSRS state: New (never reviewed), Learning (in initial reviews), Review (graduated to long intervals) |
The Stats page tracks how much time you spend studying:
Sessions are tracked automatically. The Review page starts a timer when you begin, and saves the session when you finish or leave the page.
Click the "Details" button on any word in the Words page to see its FSRS memory metrics. Here's what each metric means:
| Metric | Description |
|---|---|
| State | The card's current phase: New (never reviewed), Learning (first reviews), Review (graduated, on long intervals), or Relearning (forgotten, being re-learned). |
| Stability | How long your memory lasts, measured in days. At exactly stability days after a review, you have a 90% chance of remembering the word. Higher stability = stronger memory. |
| Difficulty | How hard this card is for you personally, on a 0-10 scale. Higher values mean the card is harder and gets shorter intervals. Unlike SM-2, difficulty uses mean reversion — it naturally decreases after consecutive correct answers. |
| Retrievability | The probability that you can recall this word right now, shown as a percentage. It starts at ~100% right after a review and decays over time following the forgetting curve. FSRS schedules reviews when this drops to ~90%. |
| Reviews | Total number of times you've reviewed this card. |
| Lapses | Number of times you forgot the card (graded "Again" after it had graduated to Review state). More lapses = the card is a "leech" that needs extra attention. |
| Last reviewed | When you last reviewed this card. |
| Next due | When FSRS has scheduled the next review. |
| Scheduled interval | The gap in days between the last review and the next due date. |
FlashVocab uses FSRS (Free Spaced Repetition Scheduler) — the most accurate open-source spaced repetition algorithm available today. Created by Jarrett Ye and backed by peer-reviewed research published at ACM KDD 2022 and IEEE TKDE 2023, FSRS represents a generational leap over the 37-year-old SM-2 algorithm still used as Anki's default.
The core promise: FSRS achieves 20-30% fewer reviews than SM-2 at the same retention rate, or higher retention with the same number of reviews. This is validated across 727 million reviews from 10,000 users in the largest spaced repetition benchmark ever conducted.
FSRS models human memory using three core variables:
| Variable | Symbol | Description |
|---|---|---|
| Difficulty | D | How hard is this card to remember? Range: 1-10. Updated after each review. |
| Stability | S | How well is the memory stored? Measured in days — the time until retrievability drops to 90%. |
| Retrievability | R | What's the probability of recall right now? Decreases over time from 100% toward 0%. |
These three variables (the DSR model) work together to determine when to schedule each card. After each review, FSRS recalculates stability based on your grade:
The forgetting curve: FSRS models forgetting as a power function — more accurate than the exponential decay used by older algorithms. Your retrievability decreases over time after each review:
Key insight: The harder the card is for you specifically, the shorter the interval. The easier, the longer. This is personalized per card based on your review history.
SM-2 was created in 1987. It's simple but has fundamental limitations by modern standards.
| Feature | SM-2 (Anki default) | FSRS (FlashVocab) |
|---|---|---|
| Created | 1987 | 2022 |
| Memory model | Single "ease factor" | 3-variable DSR model |
| Forgetting curve | Not modeled at all | Explicitly modeled (power function) |
| Personalization | None — same formula for everyone | 21 parameters optimized per user |
| Trainable parameters | 0 | 21 |
| Desired retention | Fixed (~90%, not configurable) | Configurable (70%-97%) |
| After forgetting | Reset to 1 day, ease drops 20% | Calculated post-lapse stability |
| "Ease hell" | Yes — cards get permanently stuck | No — mean reversion prevents it |
| Interval calculation | Fixed multiplier: interval * ease | Derived from predicted retrievability |
| Review efficiency | Baseline | 20-30% fewer reviews at same retention |
The "Ease Hell" problem in SM-2: Once a card's ease factor drops to the minimum (130%), it never recovers with normal use. The card appears too frequently, wasting your time forever. FSRS solves this with mean reversion — after consecutive correct answers, difficulty naturally decreases back toward baseline.
| Tool | Algorithm | Cost | FSRS Advantage |
|---|---|---|---|
| Anki | SM-2 (FSRS opt-in since 2023) | Free | FlashVocab uses FSRS by default, no configuration needed |
| SuperMemo | SM-18/19 (proprietary) | Paid | FSRS is open-source, free, and cross-platform |
| Duolingo | HLR (Half-Life Regression) | Freemium | FSRS dramatically outperforms HLR (log loss 0.34 vs 0.47) |
| Mnemosyne | Modified SM-2 | Free | Same SM-2 disadvantages as Anki |
| Quizlet | No spaced repetition | Freemium | No adaptive scheduling at all |
From the open-spaced-repetition benchmark — the largest SRS evaluation ever conducted (727 million reviews, 10,000 users):
What this means in practice:
Go to Settings to import and export words for the active deck.
Downloads all words from the active deck as a JSON file, including FSRS scheduling state. You can optionally filter by tag using the dropdown before clicking Export.
Select a JSON file to import words. The import flow includes duplicate detection:
You can share your decks with other FlashVocab users. Shared users get read-only access to view the deck and its words.
| Action | Allowed? |
|---|---|
| View deck and word list | Yes |
| Search and filter words | Yes |
| Add, edit, or delete words | No |
| Review cards | No |
| Import words | No |
| Delete or modify the deck | No |
The deck owner can unshare at any time by clicking the share button again and removing the user. Sharing is per-user — each user must be added individually.
Use cases: Teachers sharing vocabulary lists with students, study groups using the same word sets, or creating a "master deck" for new users to reference.
The Settings page provides account management and programmatic access.
API keys allow external tools (scripts, AI assistants like Claude or ChatGPT) to read and write words and decks programmatically.
Authorization: Bearer sk_your_key_hereNote: Only users with the api_user or admin role can create API keys. If you don't see the API Keys section, ask your admin to grant you the api_user role.
API keys can read/write words and decks but cannot perform destructive operations (deck delete, password change) or access review/stats endpoints — those require browser login.
Enter your current password and a new password (minimum 8 characters) to update your login credentials.
The Settings page also provides import/export for the active deck, with optional tag filtering on export. This is in addition to the Import modal available on the Words and Decks pages.
Admins can manage user accounts from the Admin page, accessible via Settings > User Management.
| Action | Description |
|---|---|
| Create user | Set display name, email, initial password, and roles |
| Edit user | Change name, email, roles, or status (active/inactive/locked) |
| Reset password | Set a new password for any user (minimum 8 characters) |
| Delete user | Permanently remove the user and all their data (decks, words, reviews). Cannot delete yourself |
| Unlock account | Change a locked user's status back to "active" after failed login lockout |
Each user can have one or more roles. Roles control what features are available:
| Role | What it grants |
|---|---|
user | Own decks, add/review words, view stats, change password |
api_user | Everything user can do, plus create and use API keys for programmatic access |
admin | Full access: user management, all features, API keys |
Users can have multiple roles at once (e.g., user + api_user).
FlashVocab includes several security measures to protect your account and data:
sk_...) is shown only once at creation time. Use the Copy button to save itadmin role. API key creation requires api_user or adminFlashVocab exposes 30+ REST endpoints for programmatic access. You can use these from scripts, AI assistants, or external tools.
All endpoints (except login and /api/spec) require authentication. Two methods are supported:
Authorization: Bearer sk_your_key_here. Generate keys in Settings.Endpoints marked JWT only require browser login — API keys are not accepted for destructive operations (deck delete, password change) or review/stats endpoints.
Fetch /api/spec (no auth required) to get a compact JSON description of every endpoint, including auth requirements, request bodies, query parameters, and response shapes. This is designed for LLMs and AI tools to discover the API programmatically.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /api/auth/login | None | Login with { email, password } |
POST | /api/auth/logout | None | Clear session cookie |
GET | /api/auth/me | JWT only | Get current user info. Returns { userId, username, email, roles } |
PUT | /api/auth/password | JWT only | Change password { currentPassword, newPassword } (min 8 chars) |
All admin endpoints require JWT with the admin role.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/admin/users | JWT (admin) | List all users |
POST | /api/admin/users | JWT (admin) | Create user { username, email, password, roles? }. Password min 8 chars. Roles is a string array, default ["user"] |
PUT | /api/admin/users/:id | JWT (admin) | Update user { roles?, status?, email?, username? } |
PUT | /api/admin/users/:id/password | JWT (admin) | Reset user password { password } (min 8 chars) |
DELETE | /api/admin/users/:id | JWT (admin) | Delete user and all their data. Cannot delete self |
Roles: Users can have multiple roles stored as an array: admin, user, api_user. The admin role grants access to user management. The api_user role (or admin) allows creating API keys.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/decks | JWT or Key | List all decks with word_count and due_count |
GET | /api/decks/:id | JWT or Key | Get deck with detailed stats |
POST | /api/decks | JWT or Key | Create deck { name, description?, source_lang?, target_lang? } |
PUT | /api/decks/:id | JWT or Key | Update deck fields |
DELETE | /api/decks/:id | JWT only | Delete deck and all its words (cascade) |
POST | /api/decks/:id/duplicate | JWT or Key | Duplicate deck with all words (FSRS state reset) |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/decks/:id/shares | JWT or Key | List users a deck is shared with (owner only) |
POST | /api/decks/:id/shares | JWT or Key | Share deck with user { email } (owner only). Returns 404 if user not found, 409 if already shared |
DELETE | /api/decks/:id/shares/:userId | JWT or Key | Unshare deck with user (owner only) |
All list/create operations require deck_id parameter.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/words?deck_id=N | JWT or Key | List words (paginated). Optional: search, tags, state, page, limit |
GET | /api/words/:id | JWT or Key | Get single word by ID |
POST | /api/words | JWT or Key | Create word { deck_id, word, translation, ... }. Returns 409 on duplicate |
POST | /api/words/batch | JWT or Key | Batch create { deck_id, words: [...] }. Skips duplicates by default |
PUT | /api/words/:id | JWT or Key | Update word fields |
DELETE | /api/words/:id | JWT or Key | Delete word |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/review?deck_id=N | JWT only | Get due cards. Optional: tags, state, mode, limit |
GET | /api/review/tags?deck_id=N | JWT only | Get all tags used in this deck |
POST | /api/review/:id | JWT only | Grade a card { rating: 1|2|3|4 } |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/stats?deck_id=N | JWT only | Full statistics for the deck |
POST | /api/stats/session | JWT only | Save study session { started_at, ended_at, duration_ms, cards_reviewed?, deck_id? } |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /api/import | JWT only | Import words { words: [...], deck_id }. Skips duplicates by default |
GET | /api/export?deck_id=N | JWT only | Export words as JSON. Optional tags filter |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /api/keys | JWT only | List all API keys |
POST | /api/keys | JWT only | Generate new key { label? }. Returns raw key (shown once) |
DELETE | /api/keys/:id | JWT only | Revoke an API key |
Each user can have one or more roles, returned as a roles array in the JWT and API responses:
| Role | Capabilities |
|---|---|
user | Own decks, review, settings. No API keys, no user management |
api_user | Same as user + can create and use API keys |
admin | Full access: manage users, own decks, review, API keys |
Multiple roles can be assigned (e.g. ["user", "api_user"]). Roles are stored as comma-separated values in the database and exposed as arrays in the API.
All errors return JSON with an error field, e.g. { "error": "deck_id is required" }
| Status | Meaning |
|---|---|
400 | Bad request (missing/invalid parameters) |
401 | Unauthorized (missing or invalid credentials) |
403 | Forbidden (insufficient permissions, e.g., non-admin accessing admin routes, or writing to a read-only shared deck) |
404 | Resource not found |
409 | Conflict (duplicate word exists) |
500 | Server error |
FlashVocab is designed to work with AI assistants (Claude, ChatGPT, etc.) for generating vocabulary words. AI-generated words can be imported into FlashVocab in two ways:
Tip: You can also tell the AI to fetch /api/spec first so it discovers the API format automatically:
"Fetch https://flashvocab.app/api/spec to understand the API, then generate 20 Spanish-Polish vocabulary words about travel."
Copy and edit the template below. The AI will output JSON ready for import. Replace YOUR_DECK_ID with your actual deck ID — you can find it on the Decks page or by calling GET /api/decks.
First, fetch the API spec to understand the exact format:
GET https://flashvocab.app/api/spec
Generate vocabulary words in this JSON structure:
{
"deck_id": YOUR_DECK_ID,
"words": [
{
"word": "el almuerzo",
"translation": "obiad (lunch)",
"example_sentence": "Vamos a tomar el almuerzo juntos.",
"example_translation": "Zjedzmy razem obiad.",
"tags": "food,A2"
}
]
}
Configuration (edit these):
- Source language: Spanish (es)
- Target language: Polish (pl)
- Deck ID: YOUR_DECK_ID (find via Decks page or GET /api/decks)
- Words per tag: 15
- Difficulty level: A2-B1
Tags/topics to generate:
- travel
- food
- daily-routine
- work
- health
Quality guidelines:
1. Words: use natural form (infinitive for verbs, singular for nouns with article)
2. Translations: most common translation in target language
3. Example sentences: natural, everyday (8-15 words)
4. Example translations: natural, not word-for-word
5. Tags: topic tag + CEFR level (A1,A2,B1,B2), comma-separated
6. No duplicates across tags
7. Mix nouns, verbs, adjectives, and common phrases
After the AI generates the words, choose one of these methods to get them into FlashVocab:
Method A — Fully automated (AI + API key):
Give the AI your API key and endpoint URL. It calls POST /api/words/batch directly — no manual steps needed.
"Fetch https://flashvocab.app/api/spec, then generate and import 20 Spanish-Polish
words about travel into deck YOUR_DECK_ID using API key sk_YOUR_API_KEY"
The AI will handle everything: generate the words, format the JSON, and POST it to the API. You can find your API key in Settings > API Keys (click Copy to copy it).
Method B — Copy-paste into FlashVocab:
Duplicates are automatically detected and listed. You can click "Import anyway" to add them as separate entries.
Method C — Upload a JSON file:
.json file.Same duplicate handling as the paste method. Both { "words": [...] } and bare array [...] formats are accepted.
Method D — curl (advanced):
# Import from file using API key
curl -X POST https://flashvocab.app/api/words/batch \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk_YOUR_API_KEY" \
-d @words.json
The batch endpoint returns { created, ids, duplicates }. Duplicates are skipped by default — pass "allow_duplicates": true in the JSON to override.
| Key | Context | Action |
|---|---|---|
Space / Enter | Review | Reveal the answer |
1 | Review (answer shown) | Grade as Again |
2 | Review (answer shown) | Grade as Hard |
3 | Review (answer shown) | Grade as Good |
4 | Review (answer shown) | Grade as Easy |
GitHub Repository — source code and issue tracker.