Developers

Everything you do in Nimbli also works from a script or an AI assistant, with the same rules and the same 25-link limit. They act as you and can't do more than you can.

Connect an AI assistant

Claude, ChatGPT or any client that speaks MCP.

In Claude, open Settings, then Connectors, then "Add custom connector". Paste the address below and sign in when asked.

https://nimbli.app/mcp

Then ask: "Shorten https://shop.example.com/spring as spring-sale." Deleting a link always asks you first. Assistants never make tokens, change teams or delete your account.

An assistant gets these tools: myTeams (the teams you're in), teamLinks (list and search a team's links), linkByCode, linkClicks, createLink, createLinks (up to 50 at once), changeDestination, setNote, renameLink, deleteLink, askForReview and reportLink.

API tokens

A token lets a script act as you, in the team you pick.

Sign in to create a token.

API basics

Base address
https://nimbli.app/api/v1 · openapi.json
Sign in
Authorization: Bearer <token>. Every call is a POST to its operation's address, with its params as JSON.
Safe retries
Send Idempotency-Key when creating; a retry with the same key returns the same link.
Limits
Shared with the dashboard: 20 new links an hour and 100 a day per team; 600 calls a minute. Every answer has RateLimit and RateLimit-Policy; when limited, Retry-After.
Paging
Lists return up to 100 items a call, with a next cursor to send back as after.

listTeams POST

The teams you're in, by name. Pass a team's id as team to make or list its links.

curl -X POST https://nimbli.app/api/v1/listTeams \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"limit":50}'

Example answer

{
  "data": [
    {
      "id": "team_01JB8XQ2F4ZK7M3N9P5R2T6V8W",
      "name": "name"
    }
  ],
  "next": "eyJrIjpbIjIwMzAtMDEtMTUiXX0"
}

setDestination POST

Changes where a link goes. Printed QR codes and shared links follow. A flagged or disabled link can't change.

curl -X POST https://nimbli.app/api/v1/setDestination \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"code":"codexxx","destination":"destination"}'

Params

code
text · required, 7 to 7 characters
destination
text · required

Example answer

{
  "id": "link_01JB8XQ2F4ZK7M3N9P5R2T6V8W",
  "code": "code",
  "name": "name",
  "destination": "https://example.com/page",
  "note": "note",
  "state": "active",
  "createdAt": "2030-01-15T09:30:00.000Z",
  "changedAt": "2030-01-15T09:30:00.000Z"
}

Errors to branch on

400blocked_destinationNimbli doesn't allow links to that site.
503check_unavailableWe couldn't check this address. Try again in a minute.
400invalid_destinationThat address is too long: 2,048 characters at most.
429limit_reachedThis team invited that address in the last day. Try again tomorrow.
409link_disabledNimbli disabled this link, so it can't be changed.
409link_flaggedYou can't change where it goes while it's flagged. Ask for a review.
400private_destinationThat address is on a private network.
400shortener_destinationNimbli links can't point to other Nimbli links.
400unsafe_destinationThis address is on Google's list of unsafe sites.

setNote POST

Sets a link's note to self, or clears it with null.

curl -X POST https://nimbli.app/api/v1/setNote \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"code":"codexxx","note":"note"}'

Params

code
text · required, 7 to 7 characters
note
text or null · required, 0 to 100 characters

Example answer

{
  "id": "link_01JB8XQ2F4ZK7M3N9P5R2T6V8W",
  "code": "code",
  "name": "name",
  "destination": "https://example.com/page",
  "note": "note",
  "state": "active",
  "createdAt": "2030-01-15T09:30:00.000Z",
  "changedAt": "2030-01-15T09:30:00.000Z"
}

Errors to branch on

400invalid_noteA note can have letters, numbers, spaces and - _ , . ' ( ) & ! ? : only.

linkClicks POST

A link's clicks per day for the last 30 days, oldest first, with the totals for 7 and 30 days.

curl -X POST https://nimbli.app/api/v1/linkClicks \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"code":"codexxx"}'

Params

code
text · required, 7 to 7 characters

Example answer

{
  "days": [
    {
      "date": "2030-01-15",
      "clicks": 3
    }
  ],
  "last7": 3,
  "last30": 3
}

Errors

Each is application/problem+json with the status, a code, a detail to show, and the field at fault.

400blocked_destinationNimbli doesn't allow links to that site.
503check_unavailableWe couldn't check this address. Try again in a minute.
400invalid_destinationThat address is too long: 2,048 characters at most.
400invalid_nameUse 3 to 50 lowercase letters, numbers, - and _.
400invalid_noteA note can have letters, numbers, spaces and - _ , . ' ( ) & ! ? : only.
429limit_reachedThis team invited that address in the last day. Try again tomorrow.
409link_disabledNimbli disabled this link, so it can't be changed.
409link_flaggedYou can't change where it goes while it's flagged. Ask for a review.
409name_takenThat name is taken. Try another.
400private_destinationThat address is on a private network.
400reserved_nameThat name is reserved. Try another.
400shortener_destinationNimbli links can't point to other Nimbli links.
409team_full
400unsafe_destinationThis address is on Google's list of unsafe sites.
401invalid_tokenThe token is unknown, revoked or expired: make another on the app's agents page.
403insufficient_scopeThe token is limited to other operations.
403outside_token_recordThe token works within one record, and this would change something outside it: outside it, the token reads only what someone signed out reads.
403first_stepIts person hasn't taken the app's first step yet, such as agreeing to its terms: `detail` says where they take it, in the app.
403token_record_leftThe token works within a record its person no longer belongs to, or that's gone. It works again if they rejoin.
429rate_limitedToo many calls: wait the seconds `Retry-After` says, then try again.
409idempotency_key_in_useA call with this `Idempotency-Key` is still running: try again in a second.
422idempotency_key_reusedThis `Idempotency-Key` was used with another body: a new request needs a new key.
400cursor_mismatch`after` came from another operation or other params: start again without it.
410version_retiredThe version was retired: move to the newest one.
400bad_request
401unauthorized
402payment_required
403forbidden
404not_found
409conflict
410gone
422unprocessable_content
429too_many_requests
500internal_server_error
503service_unavailable