Skip to content

Pin from a Private IPFS Node

The default POST /pin flow works by hunting the public DHT for peers that advertise your CID. That fails for content living on a peer that:

  • Isn't participating in the public DHT (e.g. Reprovider.Enabled = false)
  • Is behind NAT with no successful hole-punch
  • Runs on a private network you're the only one who can reach (VPN, corporate LAN, staging environment)
  • Was just published seconds ago and hasn't propagated to the DHT yet

For those cases, tell our cluster exactly where the content lives by passing one or more multiaddresses on the pin request. We swarm connect to each before the pin runs, so the DAG fetch talks directly to your peer instead of wandering the DHT.

Get your node's multiaddress

On the machine that hosts the CID, run:

bash
ipfs id

You'll see something like:

json
{
  "ID": "12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3",
  "Addresses": [
    "/ip4/127.0.0.1/tcp/4001/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3",
    "/ip4/203.0.113.42/tcp/4001/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3",
    "/ip4/203.0.113.42/udp/4001/quic-v1/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3"
  ],
  ...
}

Pick the addresses our cluster can actually reach from the internet:

  • Prefer /ip4/<your-public-ip>/tcp/4001/p2p/<PeerID> and /ip4/<your-public-ip>/udp/4001/quic-v1/p2p/<PeerID>
  • Skip /ip4/127.0.0.1/... and RFC1918 addresses (10.*, 192.168.*) — those are your local network; they'll fail from our side
  • Or use /dnsaddr/your.domain.com/tcp/4001/p2p/<PeerID> if you've published a _dnsaddr TXT record

Behind NAT?

If your machine is behind NAT, enable Kubo's AutoRelay + hole-punching (Swarm.RelayClient.Enabled = true, Swarm.EnableHolePunching = true) so we can reach it through a relay. Or run your Kubo on a small public VPS and swarm-connect there.

Pin the CID with a hint

Once you have a public multiaddress, add it to the pin request:

bash
curl -X POST https://api.ipfs.ninja/pin \
  -H "X-Api-Key: bws_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "cid": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
    "description": "staging build v42",
    "multiaddresses": [
      "/ip4/203.0.113.42/tcp/4001/p2p/12D3KooWH3uVF6wv47WnArKHk5p6cvgCJEb74UTmxztmQDc298L3"
    ]
  }'

The response includes a swarmConnected array with per-hint status:

json
{
  "cid": "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
  "status": "pinning",
  "swarmConnected": [
    {
      "multiaddr": "/ip4/203.0.113.42/tcp/4001/p2p/12D3KooWH3uV…",
      "ok": true,
      "strings": ["connect 12D3KooWH3uV… success"]
    }
  ],
  "uris": { "url": "https://ipfs.ninja/ipfs/bafybeigdyrzt5…" }
}

Multiple hints (redundancy)

Up to 5 multiaddresses per pin. If your node has several routable addresses (IPv4 + IPv6, TCP + QUIC), list them all — the first one that connects wins the block fetch:

json
{
  "cid": "bafybeigdyrzt5...",
  "multiaddresses": [
    "/ip4/203.0.113.42/tcp/4001/p2p/12D3KooWH3uV…",
    "/ip4/203.0.113.42/udp/4001/quic-v1/p2p/12D3KooWH3uV…",
    "/ip6/2001:db8::1/tcp/4001/p2p/12D3KooWH3uV…",
    "/dnsaddr/node.myapp.com/tcp/4001/p2p/12D3KooWH3uV…"
  ]
}

Interpreting swarmConnected

  • ok: true — the cluster peered with your node successfully. The pin's DAG fetch will attempt this peer first before falling back to the DHT.
  • ok: false with error: "dial to peer: no route" — network unreachable. Check that the multiaddress uses a public IP and the port isn't firewalled.
  • ok: false with error: "timeout of 5000ms exceeded" — the peer didn't respond in 5 seconds. Slow relay or offline node.
  • ok: false with error: "peer id mismatch" — the peer at that address advertises a different PeerID than the one in your multiaddress. Re-check the multiaddress.

A failing connect does not fail the pin — the pin proceeds with the DHT path. swarmConnected is diagnostic, not fatal.

Accepted multiaddress shapes

The most common shapes are supported:

TransportExample
IPv4 TCP/ip4/203.0.113.42/tcp/4001/p2p/QmPeerId
IPv6 TCP/ip6/2001:db8::1/tcp/4001/p2p/QmPeerId
IPv4 QUIC v1/ip4/203.0.113.42/udp/4001/quic-v1/p2p/QmPeerId
DNS + WSS/dns4/node.example.com/tcp/443/wss/p2p/QmPeerId
dnsaddr/dnsaddr/node.example.com/tcp/4001/p2p/QmPeerId

Must end in /p2p/<PeerID> (either legacy base58btc Qm…/12D… or CIDv1-encoded bafz…/k51…). If a legitimate multiaddress shape gets rejected, open a support ticket with the exact multiaddress and we'll widen the validator.

When NOT to use this

  • Content already on the public DHT — the default pin flow finds it fine. Don't add multiaddresses; they're only useful for peers the DHT can't reach.
  • You need attribution back to a specific peer — that's a different concern. Pin doesn't record which peer served the blocks; it just puts them on our cluster.
  • Anti-abuse / rate limiting — cap is 5 multiaddrs per pin request. If you're pinning at high volume from many nodes, run one Kubo peer that pulls from your fleet and pin from that one instead.

Reference