{"slug": "introducing-devpub-open-source-dev-to-cli-tool", "title": "Introducing DevPub - Open Source Dev.to CLI Tool", "summary": "A developer built DevPub, an open-source CLI tool for Dev.to that leverages over 40 API endpoints, including semantic search, analytics, and trend discovery, which existing tools ignore. The tool, built in a day, uses Gemini embeddings for semantic search and includes features like article validation and rate limiting. The developer also discovered undocumented API endpoints, such as the V1 Accept header and nested analytics responses.", "body_md": "Recently I went looking for a CLI tool to manage my Dev.to articles from the terminal. I write 4-5 articles per month, track analytics obsessively, and wanted a git-backed workflow.\n\nI found 9 existing tools. Tried them all. Here's what happened:\n\nEvery single tool does the same thing: publish an article. That's it. Maybe pull. Maybe validate tags.\n\nMeanwhile the Dev.to API has **40+ endpoints** including analytics, semantic search, ML-powered content concepts, follower engagement, trend tracking, and reading list management. Nobody uses them.\n\nSo I built ** devpub**.\n\n```\n# The basics (every tool does this)\ndevpub push -f articles/my-post.md\ndevpub pull\n\n# Analytics in your terminal\ndevpub stats\n# Views: 246.5K | Reactions: 4.4K | Comments: 402 | Followers: 18.9K\n\n# Full dashboard with top articles\ndevpub dashboard\n\n# AI-powered search (semantic, not keyword)\ndevpub search \"building serverless apps\" --semantic\n\n# What's trending RIGHT NOW\ndevpub trends\n\n# Catch problems before publishing\ndevpub validate\n```\n\nThe difference isn't one feature. It's coverage. Here's the comparison:\n\n| Capability | devpub | Everyone else |\n|---|---|---|\n| Publish/update articles | Yes | Yes |\n| Pull articles to local | Yes | Some |\n| Analytics (7 endpoints) | Yes | No |\n| Semantic search | Yes | No |\n| Trend discovery | Yes | No |\n| Article validation | Yes | No |\n| Rate limiting (30 req/30s) | Yes | No |\n| Retry logic for failures | Yes | No |\n| Concepts API (ML topics) | Yes | No |\n\nWhile building devpub, I found several API endpoints that aren't documented anywhere obvious:\n\n**1. Semantic Search** -- Dev.to has a full embedding-based search system using Gemini embeddings (768-dimensional vectors) with pgvector. You can search articles by *meaning*, not just keywords. The endpoint returns cosine similarity scores.\n\n**2. Concepts API** -- These are ML-generated topic classifications with daily metrics: page views, reactions, comments, popularity scores. Way more powerful than manual tags.\n\n**3. V1 Accept Header** -- The V1 API requires `Accept: application/vnd.forem.api-v1+json`\n\n. Without it, you get V0 responses. I didn't see this mentioned in any competitor's code.\n\n**4. Nested analytics responses** -- The analytics endpoints return nested objects like `{\"page_views\": {\"total\": 246454, \"average_read_time_in_seconds\": 306}}`\n\n, not flat integers. Every tool I checked either doesn't use analytics or would break on this structure.\n\nI built devpub's core in a day. Started at 2 PM on a Monday, had a working CLI by evening. Here's the honest timeline:\n\n**Hour 1-2: Research**\n\nBefore writing a single line of code, I analyzed 9 competing tools. Downloaded them, read their source, mapped which API endpoints each one used. Found that the most \"complete\" tool covered 12 out of 40+ endpoints. Most covered 3-5.\n\nThen I read the entire Forem API docs. Not the summary page that everyone reads. The full V1 spec. That's where I found semantic search, concepts, and the analytics endpoints that nobody knew existed.\n\n**Hour 3: Scaffolding**\n\n`pyproject.toml`\n\n, src layout, Click CLI entry point. Boring stuff but I got `devpub --help`\n\nworking in 15 minutes. The key decision here: use `httpx`\n\nover `requests`\n\n. httpx gives you connection pooling, proper timeouts, and the async option for later without changing the interface.\n\n**Hour 4-5: The API client**\n\nThis is where I spent the most time. Not because the HTTP calls are hard. Because I wanted the client to be production-grade from day one:\n\nThe first version didn't have any of this. It just called `raise_for_status()`\n\nand threw ugly `httpx.HTTPStatusError`\n\nexceptions at users. I caught that in testing when I pulled my own 86 articles and hit the rate limit at article 30. The whole thing crashed.\n\n**Hour 6: Testing against my real account**\n\nThis is where things got interesting. My first `devpub stats`\n\ncall crashed with:\n\n```\nTypeError: '>=' not supported between instances of 'dict' and 'int'\n```\n\nTurns out the analytics endpoint returns `{\"page_views\": {\"total\": 246454}}`\n\n, not `{\"page_views\": 246454}`\n\n. Nested dicts. No existing tool handles this correctly because no existing tool uses analytics.\n\nThe health check endpoint also surprised me. In the V1 API (with the Accept header), `/health_checks/app`\n\nrequires authentication. Without the header, it returns 401. So I changed the health check to just call `/users/me`\n\ninstead.\n\n**Hour 7: The push --all scare**\n\nDuring testing, `push --all`\n\nalmost published my README.md to Dev.to. The original logic was: find any `.md`\n\nfile with a `title`\n\nin frontmatter, push it. My README has YAML frontmatter with a title.\n\nFixed it by requiring both `title`\n\nAND `published`\n\nkeys, and only scanning known directories (`articles/`\n\n, `posts/`\n\n, `content/`\n\n, `drafts/`\n\n). Small thing, but imagine accidentally publishing your CONTRIBUTING.md as a Dev.to article.\n\n**What I'd do differently**\n\n**Start with the pull command, not push.** Pull forces you to understand the API response format before you build the data model. I built the model first based on docs, then had to fix it when real responses looked different.\n\n**Mock tests from the start.** I wrote all the code first, then tests. Should have written the API mock responses alongside the client methods. Would have caught the nested dict issue immediately.\n\n**Ship the rate limiter in v0.0.1.** I initially thought \"I'll add that later.\" Hit the limit within 30 minutes of real testing. Should have been there from commit one.\n\n```\nsrc/devpub/\n  api/        # HTTP clients with rate limiting + retries\n  cli/        # Click commands + Rich terminal output  \n  core/       # Business logic (articles, sync, validation, config)\n  templates/  # Article scaffolding (5 templates)\n```\n\nKey decisions:\n\n``` bash\n$ pytest -v\n57 passed in 1.08s\n```\n\nTests mock the HTTP layer with `respx`\n\n. No real API calls in CI. Covers:\n\n```\npip install devpub\nexport DEVPUB_API_KEY=your_key_here\ndevpub doctor\n```\n\nOr from source:\n\n```\ngit clone https://github.com/simplynadaf/devpub.git\ncd devpub\npip install -e .\n```\n\nGet your API key at: [https://dev.to/settings/extensions](https://dev.to/settings/extensions)\n\nEvery command returns structured output, handles rate limits silently, and supports `--dry-run`\n\n. AI coding agents (Claude Code, Copilot, Cursor) can use devpub as their publishing layer. The agent writes the article, devpub validates, pushes, and tracks performance. No human needed after the initial setup.\n\nThe project is in beta. PRs are welcome. Some things that need help:\n\n`--graph`\n\nflag is accepted but not implemented yetCheck the [issues](https://github.com/simplynadaf/devpub/issues) for `good first issue`\n\nlabels.\n\n**GitHub**: [github.com/simplynadaf/devpub](https://github.com/simplynadaf/devpub)\n\nIf this saves you time, star the repo. If something's broken, open an issue. If you want a feature, send a PR.\n\nWhat's your current Dev.to workflow? Are you writing in the browser editor, or do you have a local setup? Curious what pain points people are hitting.\n\n📺 [Watch the full demo on YouTube](https://youtu.be/u8H2BITfYjc)\n\n*Built by* [Sarvar Nadaf](https://sarvarnadaf.com) - Cloud Architect\n\n*Follow me:* [Dev.to](https://dev.to/sarvar_04) | [GitHub](https://github.com/simplynadaf) | [YouTube](https://www.youtube.com/@TechwithSarvar) | [LinkedIn](https://linkedin.com/in/sarvarnadaf)", "url": "https://wpnews.pro/news/introducing-devpub-open-source-dev-to-cli-tool", "canonical_source": "https://dev.to/sarvar_04/introducing-devpub-open-source-devto-cli-tool-49jf", "published_at": "2026-08-01 12:10:00+00:00", "updated_at": "2026-08-01 12:10:47.682891+00:00", "lang": "en", "topics": ["developer-tools", "artificial-intelligence", "machine-learning"], "entities": ["DevPub", "Dev.to", "Forem", "Gemini", "httpx", "Click"], "alternates": {"html": "https://wpnews.pro/news/introducing-devpub-open-source-dev-to-cli-tool", "markdown": "https://wpnews.pro/news/introducing-devpub-open-source-dev-to-cli-tool.md", "text": "https://wpnews.pro/news/introducing-devpub-open-source-dev-to-cli-tool.txt", "jsonld": "https://wpnews.pro/news/introducing-devpub-open-source-dev-to-cli-tool.jsonld"}}