Bring your scattered bookmarks home to mymind.
tomymind is a small command-line tool that imports your bookmarks from
other sites into your mymind library. It runs entirely on your machine,
authenticates as the real you (using your own browser session — no
third-party API keys), and pushes each URL to mymind via the official
API.
Sources supported today: X (Twitter) — Instagram and Pinterest on the roadmap.
Important
This is a personal project, not an official mymind product. It talks to the public mymind API using your own credentials. No affiliation, no endorsement.
Use is at your own risk. Each source has its own Terms of Service,
and automated access may not be permitted on every platform. Only run
tomymind with accounts you own, only to export your own bookmarks,
and make sure your usage complies with each service's ToS. The tool is
provided "as is" without warranty — see LICENSE.
- How it works
- Quick start
- Requirements
- Installation
- Configuration
- Using it — worked example with X
- Command reference
- What stays on your machine
- Troubleshooting
- Adding a new source
- Roadmap
- Contributing
- Acknowledgments
- License
Every source follows the same three steps, each triggered manually:
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ 1. Authenticate │ ─▶ │ 2. Fetch │ ─▶ │ 3. Push │
│ │ │ │ │ │
│ paste cookies │ │ headless browser │ │ POST /objects │
│ from your Chrome │ │ loads + reads │ │ with HS256 JWT │
│ (or manual login)│ │ your bookmarks │ │ rate-limit aware │
└──────────────────┘ └──────────────────┘ └──────────────────┘
│ │ │
▼ ▼ ▼
sessions/<src>/ output/<src>.json mymind.com library
What that means in practice:
- You stay in control. Nothing happens in the background. Every step is a command you type.
- Your data stays local until you explicitly
push. The fetch phase writes a plain JSON file you can read, edit, or archive. - mymind does the rest. We only send URLs. Title, screenshot, preview, summary, tags — mymind extracts all of that server-side, just like when you save via the browser extension.
- It's resumable. Ctrl+C during a push and re-run: it picks up exactly where it stopped, thanks to a local ledger.
git clone https://github.com/akhalildjo/tomymind.git
cd tomymind
make install
# One-time: get your mymind API credentials and put them in .env
cp .env.example .env
# then open .env in your editor and fill in MYMIND_API_KEY_ID + MYMIND_API_KEY_SECRET
# Per-source flow (X example):
make import-cookies-x # paste auth_token + ct0 from your logged-in Chrome
make fetch-x # writes output/x_bookmarks.json
make push-x # POSTs each URL to mymindThat's the whole thing.
- Python 3.12 or newer
- uv — fast, reproducible Python package manager
- A browser where you're already logged into the source you want to read from (Chrome recommended)
- (optional) GNU Make — provides
shortcut commands. On Windows:
winget install GnuWin32.Makeorscoop install make. Skip it if you prefer typing the fulluv run tomymind …commands.
CI runs lint + the full test suite on every push, across the matrix below. The pure helpers and mocked plumbing are exercised on each cell; the real Chromium / real mymind paths aren't E2E-tested by design (see Roadmap → Test coverage for the rationale).
| OS | Python | Status |
|---|---|---|
| Linux | 3.12 / 3.13 / 3.14 | ✓ Daily-driven by the maintainer |
| macOS | 3.12 / 3.13 / 3.14 | ✓ CI-validated, not daily-driven — field reports welcome |
| Windows | 3.12 / 3.13 / 3.14 | ✓ CI-validated, not daily-driven — field reports especially welcome |
If something works for you (or breaks in an interesting way), please share via Discussions or an issue — that's how we'll prioritize the remaining test gaps.
git clone https://github.com/akhalildjo/tomymind.git
cd tomymind
make installUnder the hood this runs:
uv sync --extra source --extra automation --extra push --extra dev
uv run playwright install chromiumDon't have Make? Run the two commands above directly.
Playwright ships with Chromium, but some sources detect it as a non-real browser. Using real Google Chrome works better. If you already have Chrome installed on your system, Playwright will find it automatically. Otherwise:
make install-chromeNote
On Windows, this needs an Administrator terminal because Playwright installs Chrome system-wide. Skip if you've already got Chrome from chrome.com.
You need two credentials from mymind to use push. Get them from your
mymind account settings (look for "API" or "Developer"). You'll receive:
- A Key ID — a short identifier (e.g.
bmtw…) - A Private key — a base64-encoded HMAC secret, ending in
=
Drop them into .env:
cp .env.example .envThen edit .env:
MYMIND_API_KEY_ID=your-key-id-here
MYMIND_API_KEY_SECRET=your-base64-secret-here=Paste both values verbatim — no quotes, no spaces. The trailing =
on the secret is base64 padding and must be preserved.
Caution
Your MYMIND_API_KEY_SECRET grants full read/write access to your
mymind library. Treat it like a password. Never commit .env, never
paste it in a chat or screenshot, never share it. If it leaks,
regenerate the pair in your mymind settings.
X currently doesn't accept automated browser logins. The workaround is to copy your existing session cookies from a Chrome window where you're already logged in.
make import-cookies-xYou'll be prompted to paste two cookies. To find them:
- Open Chrome and go to x.com (make sure you're logged in)
- Press F12 to open DevTools
- Click the Application tab (you may need to expand
>>to see it) - In the left sidebar: Cookies → https://x.com
- Find and copy the Value for these two cookies:
auth_token— a long hexadecimal stringct0— a longer hexadecimal string
Paste each at the prompt. The tool verifies your session before exiting, so a wrong or expired value fails fast instead of mysteriously later.
Tip
Cookies have a 30-day lifetime by default. When the tool starts
reporting "Session expired", re-run make import-cookies-x to refresh.
For sources that allow automated logins (X currently does not), you can use:
make login-xThis opens a visible Chromium window. Log in normally, then return to the terminal and press ENTER to save the session.
make fetch-xYou'll see progress streaming live:
[ 1] https://x.com/jack/status/1234567890
[ 2] https://x.com/openai/status/1234567891
…
[ 412] https://x.com/anthropicai/status/1234568302
Done. 412 bookmarks → output/x_bookmarks.json
For more control:
uv run tomymind fetch x --limit 100 # cap to first 100
uv run tomymind fetch x --show-browser # watch the browser work
uv run tomymind fetch x --output ./my.json # custom output pathmake push-x 412 fetched, 0 already pushed, 412 to send.
[ 1/412] NEW https://x.com/jack/status/1234567890
[ 2/412] NEW https://x.com/openai/status/1234567891
[ 3/412] EXISTED https://x.com/somefriend/status/9999999
…
Done. 408 created, 3 already in mymind, 1 failed.
Each line tells you what happened:
| Tag | Meaning |
|---|---|
NEW |
A new object was created in your mymind library (HTTP 201) |
EXISTED |
mymind already had this URL — it just refreshed the timestamp (HTTP 200) |
FAIL XXX |
An error; the URL is skipped and you can investigate the status code |
The pusher automatically:
- Retries 429 (rate limit) by sleeping until your slowest credit bucket resets
- Retries 5xx errors with exponential backoff (2s, 4s, 8s)
- Writes a local ledger (
output/.pushed_x.json) after each success - Resumes from the ledger on re-run, so Ctrl+C is safe
tomymind sources List supported sources
tomymind login <source> Manual login flow (visible browser)
tomymind import-cookies <source> Paste cookies from a logged-in browser
tomymind fetch <source> [options] Fetch bookmarks to JSON
tomymind push <source> [options] Push JSON to mymind
fetch options:
--limit N Stop after N bookmarks
--show-browser Run with a visible browser window
--output PATH Custom output JSON path
push options:
--input PATH Custom input JSON path
--ledger PATH Custom ledger path
Or via Make:
make import-cookies-x make fetch-x make push-x
make login-x make check make help
Everything sensitive is stored locally and is gitignored:
| File / folder | Contents |
|---|---|
sessions/<source>/ |
Persistent Chrome profile (cookies, localStorage) |
output/<source>_bookmarks.json |
Fetched bookmarks |
output/.pushed_<source>.json |
Local ledger of already-pushed IDs |
.env |
Your mymind API credentials |
The tool makes network calls only to:
- The source's website during the fetch (same traffic as a normal browser visit)
https://api.mymind.com/objectsduring push
No analytics, no telemetry, no third-party services.
"Session expired" right after import-cookies
- Double-check you copied both cookies (
auth_tokenANDct0) — one without the other won't authenticate. - Make sure you copied the Value column, not the Name or any other field.
- The session may have been revoked since you copied it (e.g. you logged out in the other browser). Log in again in Chrome and re-copy.
"system Chrome not found, falling back to bundled Chromium"
This is a warning, not an error. The cookie-import flow works fine with
Chromium. If you want the warning gone, install real Google Chrome from
chrome.com or run make install-chrome.
Fetch finishes with 0 items
- Your session may have expired mid-run. Re-run
make import-cookies-x. - The source might have changed its HTML structure. Open an issue with the source name and your output.
"MYMIND_API_KEY_ID and MYMIND_API_KEY_SECRET must be set"
You haven't created .env yet, or the values are blank. See
Configuration.
Push fails with 401 Unauthorized
Your credentials are wrong or revoked. Regenerate them in your mymind
settings and update .env. Note: MYMIND_API_KEY_SECRET must be the
base64 value mymind gives you (typically ends in =), pasted
verbatim with no transformations.
Push appears stuck for minutes
You probably hit the rate limit. mymind's POST /objects costs 10–250
credits per call, and your account has both a burst budget and a
30-day sustained budget. The pusher sleeps until the slowest bucket
resets and resumes automatically. You can Ctrl+C and resume later —
the ledger has your progress.
The architecture is shaped so new sources only touch
src/tomymind/sources/. Briefly:
- Create
src/tomymind/sources/<name>.pywith a class subclassingBaseSource. Implementis_logged_in()andfetch(). - Register it in
src/tomymind/sources/__init__.py. - Optionally declare
cookie_import_specsif the source doesn't accept automated browser logins.
See CLAUDE.md for the full contract and conventions, or
src/tomymind/sources/x.py as a worked
reference.
External PRs aren't open yet — see Contributing.
- X (Twitter) source —
/i/bookmarkswith infinite scroll - Instagram source —
/<user>/saved/ - Pinterest source —
/<user>/_saved/ - YouTube "Watch later" → mymind
- Repo skeleton, base source runner, output schema
- Cookie-import flow for sources that don't accept automated logins
-
tomymind push— HS256 JWT signer, rate-limit-aware client, resumable ledger
The unit suite covers pure helpers and mocked plumbing on every CI matrix cell. Known gaps, in ROI order:
-
httpx.MockTransporttests forMymindClient.create_objectcovering the 200 / 201 / 429-retry / 5xx-retry paths - One real-Playwright smoke test on Linux to validate
launch_persistent_context+add_cookiesbindings against a local HTML fixture (no live third-party site) - CI job that runs
make checkon Windows so the Makefile'scmd.exebranch is exercised end-to-end (today onlyuv run pytestis) - Test for the
channel="chrome"system-Chrome lookup with the "Chrome not found → bundled Chromium" fallback on each OS - Repro of Windows session-dir file-locking behavior so we catch the failure mode before users do
Live x.com / instagram.com / pinterest.com E2E tests are
deliberately out of scope — live pages change too often, see
CLAUDE.md for the rationale.
Want a source added? Open an issue.
Note
External code contributions aren't open yet. tomymind is still
being shaped by its initial maintainer — release process, source
roadmap, and a few internal conventions are still settling. Pull
requests from outside the maintainer team will be closed with a
pointer to CONTRIBUTING.md. This may change in
the future — follow the repo for a heads-up.
What you can do right now:
- Report bugs or request new sources via the issue templates. Expect a slower-than-average triage while contributions are closed.
- Report security issues privately via
GitHub Security Advisories —
these are always welcome. See
SECURITY.md.
For full details (including the maintainer release process and what
contributing will look like once it opens) see
CONTRIBUTING.md. For architecture and the
debugging playbook, see CLAUDE.md.
Huge thanks to Tobias van Schneider and the mymind team for building a bookmark home that actually respects its users — no algorithmic feed, no ads, no dark patterns — and for opening up a public API that makes a personal tool like this one possible in the first place.
This project also stands on the shoulders of the open-source ecosystem: Playwright, Pydantic, Typer, httpx, uv, ruff, and pytest — thanks to every maintainer who keeps those alive.
And finally, thanks to every contributor who files an issue, opens a PR, or just stars the repo. Every bit helps.
MIT — see LICENSE.
A personal project for mymind users. mymind® is a trademark of mymind GmbH; X® is a trademark of X Corp. This project is not affiliated with, endorsed by, or sponsored by either.