likeslikes

Adding likes to your blog

This page is the canonical, always up to date guide. Two ways to integrate: the drop-in widget (two lines of HTML) or the direct API if you prefer to build your own button. Either way, start with the setup below.

1. Get set up

  1. Register an account — create one here, it is free.
  2. Create a project — on the dashboard, give it a name like “My blog”. A project groups the domains that belong to the same site, and analytics are aggregated per project.
  3. Add and verify your domain — add your domain to the project. You will get a verification token that looks like likes-<32 hex chars>. Prove the domain is yours with either method, then add the domain again:

    Option A — DNS TXT record (checked first)

    likes-verification=likes-a1b2c3...32charhash

    Add it on the domain itself or on its root domain — subdomains fall back to the root, and multi-part TLDs like .com.es and .co.uk are handled correctly.

    Option B — HTML meta tag (fallback, checked on your homepage)

    <meta name="likes-verification" content="likes-a1b2c3...32charhash">
  4. Integrate — paste the widget below, or call the API directly.

2. The widget recommended

Drop these two lines where you want the like button to appear:

<div id="likes"></div>
<script src="https://likeslikes.net/likes.js" defer></script>

The widget renders a heart with the like count for the current page and lets each visitor like it once. A canonical URL is used when present. If its domain is not registered with Likeslikes, the widget tries the URL being visited instead.

Options

#likes { --likeslikes-color: #7c3aed; }

How the page URL is chosen

The widget uses the first available URL in this order: a container's data-url, the script's data-url, then the canonical URL (unless ignored). If a canonical on another origin returns Domain not found, the widget retries with the current origin and path. Explicit data-url values do not fall back automatically.

<div id="likes"></div>
<script src="https://likeslikes.net/likes.js" data-ignore-canonical defer></script>

Hugo and republished posts

Set Hugo's baseURL to the public domain visitors use so generated links, metadata, and sitemaps use the verified domain. To make every hosting alias count likes for that public domain, set a fixed per-page data-url in your layout or partial:

<div id="likes" data-url="{{ .Permalink }}"></div>
<script src="https://likeslikes.net/likes.js" defer></script>

Troubleshooting "Domain not found"

When neither the canonical domain nor the current domain is registered, the widget remains visible as a disabled counter showing 0 and logs the error in the browser console. Register the fallback hostname or use data-url to select a verified URL.

3. The direct API

Building your own button (a static site, a Hugo template, anything)? There are only two endpoints, and you always pass the full URL of the post — the domain is extracted automatically, no IDs or keys needed.

Get the like count for a URL

curl "https://likeslikes.net/api/v1/likes?url=https://myblog.com/my-post"

Returns {"likes": 5, "is_liked": false, ...} — is_liked tells whether the calling IP has already liked this URL.

Add a like

curl -X POST https://likeslikes.net/api/v1/likes \
  -H "Content-Type: application/json" \
  -d '{"url": "https://myblog.com/my-post"}'

One like per visitor per URL; duplicates return an error.

A minimal JavaScript button

<button id="like-btn">❤ <span id="like-count">…</span></button>

<script>
const API = 'https://likeslikes.net';
const url = window.location.href;
const btn = document.getElementById('like-btn');
const count = document.getElementById('like-count');

fetch(`${API}/api/v1/likes?url=${encodeURIComponent(url)}`)
  .then(r => r.json())
  .then(({ data }) => {
    count.textContent = data.likes;
    btn.disabled = data.is_liked;
  });

btn.addEventListener('click', () => {
  fetch(`${API}/api/v1/likes`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ url })
  })
    .then(r => r.json())
    .then(({ data }) => {
      if (data.success) {
        count.textContent = data.likes;
        btn.disabled = true;
      }
    });
});
</script>

Can I export my data?

Yes. Your data belongs to you, and you can take it with you whenever you want. On the dashboard, open a project's actions menu and choose Export data (JSON). The download includes the project, its domains, and one record per like with its URL and timestamp. Use it in another service or your own tools. Export and check your downloaded file before deleting a project; deletion cannot be undone.

Exports contain no IP addresses, IP hashes, or visitor identifiers. Each entry in likes represents one recorded like. Two entries can have the same URL and timestamp and still represent separate likes; count both.

JSON format version 1 contains format_version, exported_at (UTC), project (id, name, public_id), domains (id, domain_name, created_at), and likes (url, created_at). URLs use HTTPS with the recorded domain and normalized path; the original scheme, query strings, and fragments are not retained. Record timestamps use YYYY-MM-DD HH:MM:SS in the database's timezone, without an offset, and may be null for older records. Empty collections are arrays.

Each export is a full snapshot; adding multiple exports together would double-count likes. There is currently no import feature, and exports cannot restore IP-based duplicate checks.

Good to know