How I spent 3 months turning a simple CLI into something I'm genuinely proud of
Where We Left Off
Back in November, I wrote about 404ping v2 โ my lightweight API testing CLI that was basically "curl with memory."
It could:
- Send HTTP requests (obviously)
- Store variables globally or per-collection
- Save requests to collections
- Run saved requests with overrides
- Show different output modes (headers, connection info, TLS details)
People actually used it. Bug hunters messaged me. Developers starred it on GitHub. I was happy.
But then I kept using it. And every time I reached for 404ping in my actual workflow, I'd hit a wall and think: "Why can't it just...?"
So I spent the next three months answering that question.
The "Why Can't It Just..." Moments
Moment 1: "Why can't it just SAVE the damn request while I'm testing it?"
The old way:
# Test the request first
404ping request {{host}}/api/login -X POST -d '{"email":"test@test.com"}'
# Looks good? Now save it (copy-paste hell)
404ping collection save myapp login -X POST {{host}}/api/login -d '{"email":"test@test.com"}'
I was literally typing the same request twice. That's stupid.
What I built: Quick Save
# Test AND save in one command
404ping request {{host}}/api/login -X POST \
-d '{"email":"test@test.com"}' \
--save myapp.login
One flag. Done. The request you just tested is now saved. No copy-paste. No context switching.
This single feature changed how I use 404ping. Now I just add --save to everything and build my collection as I explore APIs.
Moment 2: "Why can't it just validate the response automatically?"
I was constantly doing this:
404ping run myapp:login
# Check status code manually
# Check if token exists manually
# Grep for specific values manually
What I built: Inline Assertions
404ping request https://api.example.com/users/42 \
--assert "status=200" \
--assert "json.userId=42" \
--assert "json.role=admin"
Now 404ping tells me if something's wrong. Perfect for smoke tests or CI/CD:
404ping run myapp:login --assert "status=200" --assert "json.token!=null"
# Exit code 0 if passes, 1 if fails
Moment 3: "Why can't it just use the token from the previous request?"
This was the big one. The workflow that made me want to rebuild everything:
Before (the painful way):
# 1. Login
404ping run myapp:login
# Response: {"token": "eyJhbGc..."}
# 2. Manually copy token
404ping set token:eyJhbGc...
# 3. Use token in next request
404ping run myapp:get-profile
Every. Single. Time. Manual token copying. This is 2025, why am I doing this?
What I built: Request Chaining & Variable Extraction
404ping sequence auth:login users:me \
--extract token=json.token \
--bearer {{runtime.token}} \
--assert "status=200"
What happens:
- Runs
auth:login - Extracts
json.tokenfrom response โ stores as{{runtime.token}} - Runs
users:mewithAuthorization: Bearer {{runtime.token}}header - Asserts both succeeded
No manual copying. No intermediate steps. Just declare the flow and it works.
Real-world example:
404ping sequence \
auth:login \
users:create \
posts:create \
posts:list \
--extract token=json.auth.token \
--extract userId=json.user.id \
--bearer {{runtime.token}} \
--assert "status=200"
This is what made 404ping actually useful for real workflows.
Moment 4: "Why can't it just parse the JSON for me?"
I was piping everything to jq:
404ping run myapp:users | jq '.data.users[].email'
What I built: Built-in JSON Filtering
404ping request https://api.example.com/posts/1 \
--filter "json.comments[*].{author: author.name, text: body}"
And you can extract values directly into variables:
404ping request https://api.example.com/auth \
--extract token=json.access_token \
--extract userId=json.user.id \
--extract trace=header.X-Trace-Id
Extracted values go into {{runtime.*}} and can be used in chained requests.
Moment 5: "Why can't it just switch environments easily?"
Testing across staging/production was painful:
Before:
404ping set host:https://staging-api.myapp.com
# Test stuff
404ping set host:https://api.myapp.com
# Test stuff
What I built: Environment Profiles
Create .env.staging:
BASE_URL=https://staging-api.myapp.com
API_KEY=staging_key_123
TIMEOUT=10000
Create .env.production:
BASE_URL=https://api.myapp.com
API_KEY=prod_key_456
TIMEOUT=5000
Now switch with one flag:
404ping --env staging run myapp:users
404ping --env production run myapp:users
You can even layer multiple env files:
404ping --env staging --env-file secrets/.overrides.env run billing:charge
Moment 6: "Why can't it just work with Postman collections?"
My team uses Postman. I wanted to use 404ping. We were stuck in different worlds.
What I built: Postman Import/Export
# Import their collection
404ping postman import team-api.postman.json -c teamapi
# Work in terminal with 404ping's speed
404ping run teamapi:login
404ping run teamapi:create-user --assert "status=201"
# Export back to share updates
404ping postman export teamapi -o team-api.postman.json
Now I can:
- Import team's Postman collections
- Work faster in terminal
- Export when I need to share
- Best of both worlds
Moment 7: "Why can't it just run scripts before/after requests?"
Sometimes you need custom logic โ dynamic signatures, complex validation, custom logging.
What I built: Hooks & Scripts
404ping request https://api.example.com/orders \
--pre-script @scripts/add-signature.js \
--post-script "console.log('Order created:', response.data.orderId)"
Pre-scripts can modify the request before it's sent.
Post-scripts can validate, log, or extract data from responses.
Moment 8: "Why can't it just benchmark this endpoint?"
Before: Install ab or wrk, figure out syntax, parse output...
Now:
404ping request https://api.example.com/heavy \
--benchmark 10
Runs it 10 times and shows:
- Min/Median/P95 latency
- Success rate
- Average response size
Perfect for quick performance checks.
What 404ping Actually Is Now
Let me show you a real workflow โ the kind I run every day as a bug bounty hunter:
Scenario: Testing a new API target
# 1. Setup environment
404ping set host:https://api.target.com
# 2. Test and save login endpoint
404ping request {{host}}/auth/login -X POST \
-d '{"email":"test@test.com","password":"password"}' \
--save target.login \
--assert "status=200"
# 3. Run a full authenticated flow
404ping sequence target:login \
https://{{host}}/users/me \
https://{{host}}/admin/dashboard \
--extract token=json.access_token \
--bearer {{runtime.token}} \
--assert "status=200"
# 4. Found IDOR? Test multiple user IDs quickly
404ping run target:get-user -u "{{host}}/users/123" --assert "status=403"
404ping run target:get-user -u "{{host}}/users/456" --assert "status=403"
404ping run target:get-user -u "{{host}}/users/789" --assert "status=200"
# 5. Check TLS configuration
404ping request {{host}}/secure --tls
# 6. Benchmark for timing attacks
404ping request {{host}}/auth/check -d '{"user":"admin"}' --benchmark 100
That's a complete security test workflow in 6 commands. No Postman. No bash scripts. Just 404ping.
The Complete Feature List (What You Can Do Now)
Core Functionality:
- โ HTTP requests (all methods, headers, bodies)
- โ Global and collection-scoped variables
- โ Collections for organizing requests
- โ
Quick save with
--saveflag - โ Run saved requests with overrides
New Superpowers:
- ๐งช Assertions โ Validate responses inline (
--assert) - ๐ Request Chaining โ Run sequences with variable extraction (
sequence) - ๐ JSON Filtering โ Parse responses without jq (
--filter) - ๐พ Variable Extraction โ Capture values from responses (
--extract) - ๐ Environment Profiles โ Switch environments with
--env - ๐ Postman Import/Export โ Work with team collections
- ๐ช Hooks & Scripts โ Pre/post request JavaScript (
--pre-script,--post-script) - โก Benchmarking โ Performance testing built-in (
--benchmark) - ๐ Advanced Output Modes โ
--info,--connection,--tls,--debug
Security & Developer Experience:
- ๐ก๏ธ SSL verification by default (explicit
--insecureto bypass) - ๐ Comprehensive error messages that actually help
- ๐ TLS/SSL certificate inspection
- ๐ Connection debugging
- โ Input validation and path traversal protection
How to Use It (Quick Start)
Installation
git clone https://github.com/toklas495/404ping.git
cd 404ping
npm install
npm run build
Basic Usage
# Simple request
404ping request https://api.example.com/users
# POST with data
404ping request https://api.example.com/login -X POST \
-d '{"email":"test@test.com","password":"secret"}'
# Save while testing
404ping request {{host}}/api/login -X POST \
-d '{"email":"{{email}}"}' \
--save myapp.login
# Run saved request
404ping run myapp:login
# Chain requests with token extraction
404ping sequence myapp:login myapp:profile \
--extract token=json.token \
--bearer {{runtime.token}}
# Switch environments
404ping --env staging run myapp:users
# Import Postman collection
404ping postman import api.postman.json -c myproject
Real Workflow
# 1. Setup
404ping set host:https://api.myapp.com
# 2. Test & save requests as you explore
404ping request {{host}}/auth/login -X POST \
-d '{"email":"dev@myapp.com","password":"test123"}' \
--save myapp.login \
--assert "status=200"
# 3. Run authenticated flows
404ping sequence myapp:login myapp:get-profile myapp:list-posts \
--extract token=json.access_token \
--bearer {{runtime.token}} \
--assert "status=200"
# 4. Debug issues
404ping run myapp:slow-endpoint --info --benchmark 5
# 5. Check security
404ping request {{host}}/secure --tls
The Numbers
Since v2:
| Metric | Change |
|---|---|
| Features | +8 major features |
| Commands |
request, run, set, vars โ Added sequence, postman
|
| Flags | +12 new flags (--assert, --extract, --filter, --benchmark, --env, etc.) |
| GitHub Stars | 6 โ Growing |
| Use Cases | Testing โ Testing + CI/CD + Security Audits + Performance Testing |
What I Learned
1. Listen to your own frustration
Every feature came from me getting annoyed using my own tool. The best product feedback is your own pain.
2. "Just one more flag" compounds quickly
--save seemed small. But it changed everything. Then --extract. Then --assert. Small conveniences add up to workflows you couldn't imagine before.
3. Interoperability > Lock-in
Adding Postman import/export wasn't admitting defeat. It made 404ping more valuable. Now teams can use both.
4. Security should be the default
SSL verification on by default. Explicit --insecure flag. If you want to be insecure, you have to type it out.
5. Documentation is code
Every new feature came with examples in the README. If users can't figure it out in 30 seconds, it might as well not exist.
What's Next
I'm not done. Here's what I'm thinking about:
Maybe Soon:
- ๐ Plugin system โ Let people extend 404ping however they want
- ๐ Request history โ See what you ran recently, replay it
- ๐งฌ Response diffing โ Compare staging vs production responses
- ๐ฏ Mock server โ Spin up mocks from saved responses
Ideas I'm Exploring:
- GraphQL support
- gRPC testing
- WebSocket connections
- Request templating
But I'm not rushing. I'd rather ship features that work perfectly than half-bake 10 things.
Try It
If you test APIs and you're tired of:
- Postman eating 600MB of RAM
- Copying tokens between requests manually
- Writing bash scripts to chain API calls
- Remembering curl flags
Give 404ping a shot:
git clone https://github.com/toklas495/404ping.git
cd 404ping
npm install && npm run build
404ping --help
โญ GitHub: github.com/toklas495/404ping
Final Thoughts
v2 was "curl with memory."
Now? 404ping is actually good. It's the tool I reach for first. It's fast, it's powerful, and it gets out of my way.
If you try it and hate something, tell me. If you love it, tell your friends.
And if you have ideas? Open an issue. I'm always looking for the next "why can't it just..." moment.
Happy hunting ๐ฏ
โ Nimesh (toklas495)
P.S. โ If you're using 404ping for bug bounties or in production, I'd genuinely love to hear your story. Success stories fuel late-night coding sessions.
Top comments (0)