<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet href="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/rss-style.xsl" type="text/xsl"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	    xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	     xmlns:dc="http://purl.org/dc/elements/1.1/"
	   xmlns:atom="http://www.w3.org/2005/Atom"
	     xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	  xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>
<channel>
	<title>FourSquare &#8211; Terence Eden’s Blog</title>
	<atom:link href="https://shkspr.mobi/blog/tag/foursquare/feed/" rel="self" type="application/rss+xml" />
	<link>https://shkspr.mobi/blog</link>
	<description>Regular nonsense about tech and its effects 🙃</description>
	<lastBuildDate>Tue, 02 Jun 2026 07:03:18 +0000</lastBuildDate>
	<language>en-GB</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://shkspr.mobi/blog/wp-content/uploads/2023/07/cropped-avatar-32x32.jpeg</url>
	<title>FourSquare &#8211; Terence Eden’s Blog</title>
	<link>https://shkspr.mobi/blog</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title><![CDATA[Using FourSquare's API to post location checkins to social media]]></title>
		<link>https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/</link>
					<comments>https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 02 Jun 2026 11:34:51 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[BlueSky]]></category>
		<category><![CDATA[FourSquare]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[MastodonAPI]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=68230</guid>

					<description><![CDATA[What is this, 2016?  I like sharing my location with my pocket friends sometimes. If I&#039;m in a cool bar that they know, perhaps they can recommend a drink. If they live nearby, maybe they want to come for dinner. Not everyone has FourSquare&#039;s SwarmApp, so it is handy to automatically share its updates with other people.  Of course, Swarm doesn&#039;t cross-post to social media because walled-gardens…]]></description>
										<content:encoded><![CDATA[<p>What is this, 2016?</p>

<p>I like sharing my location with my pocket friends sometimes. If I'm in a cool bar that they know, perhaps they can recommend a drink. If they live nearby, maybe they want to come for dinner. Not everyone has FourSquare's SwarmApp, so it is handy to automatically share its updates with other people.</p>

<p>Of course, Swarm doesn't cross-post to social media because walled-gardens are the most profitable. This is my attempt to open it back up again.</p>

<p>Here's what they look like on BlueSky and Mastodon:</p>

<blockquote class="bluesky-embed" data-bluesky-uri="at://did:plc:i6misxex577k4q6o7gloen4s/app.bsky.feed.post/3mmlb3yva3b2x" data-bluesky-cid="bafyreifyyuioy5zwpghfyqcdx7pjippygsg3o3nr3svtvf7owqsdjlygxy" data-bluesky-embed-color-mode="system"><p lang="en">Checked in to Hamburger Fischmarkt, Große Elbstr. 9 (Fischmarkt), Germany

Probably a *bit* early for a breakfast beer.
See on Swarm<br><br><a href="https://bsky.app/profile/did:plc:i6misxex577k4q6o7gloen4s/post/3mmlb3yva3b2x?ref_src=embed">[image or embed]</a></p>— Terence Eden (<a href="https://bsky.app/profile/did:plc:i6misxex577k4q6o7gloen4s?ref_src=embed">@edent.tel</a>) <a href="https://bsky.app/profile/did:plc:i6misxex577k4q6o7gloen4s/post/3mmlb3yva3b2x?ref_src=embed">24 May 2026 at 07:45</a></blockquote>

<script async="" src="https://embed.bsky.app/static/embed.js" charset="utf-8"></script>

<blockquote class="mastodon-embed" data-embed-url="https://mastodon.social/@Edent/116642179645011519/embed" style="background: #FCF8FF; border-radius: 8px; border: 1px solid #C9C4DA; margin: 0; max-width: 540px; min-width: 270px; overflow: hidden; padding: 0;"> <a href="https://mastodon.social/@Edent/116642179645011519" target="_blank" style="align-items: center; color: #1C1A25; display: flex; flex-direction: column; font-family: system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', Roboto, sans-serif; font-size: 14px; justify-content: center; letter-spacing: 0.25px; line-height: 20px; padding: 24px; text-decoration: none;"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32" height="32" viewBox="0 0 79 75"><path d="M63 45.3v-20c0-4.1-1-7.3-3.2-9.7-2.1-2.4-5-3.7-8.5-3.7-4.1 0-7.2 1.6-9.3 4.7l-2 3.3-2-3.3c-2-3.1-5.1-4.7-9.2-4.7-3.5 0-6.4 1.3-8.6 3.7-2.1 2.4-3.1 5.6-3.1 9.7v20h8V25.9c0-4.1 1.7-6.2 5.2-6.2 3.8 0 5.8 2.5 5.8 7.4V37.7H44V27.1c0-4.9 1.9-7.4 5.8-7.4 3.5 0 5.2 2.1 5.2 6.2V45.3h8ZM74.7 16.6c.6 6 .1 15.7.1 17.3 0 .5-.1 4.8-.1 5.3-.7 11.5-8 16-15.6 17.5-.1 0-.2 0-.3 0-4.9 1-10 1.2-14.9 1.4-1.2 0-2.4 0-3.6 0-4.8 0-9.7-.6-14.4-1.7-.1 0-.1 0-.1 0s-.1 0-.1 0 0 .1 0 .1 0 0 0 0c.1 1.6.4 3.1 1 4.5.6 1.7 2.9 5.7 11.4 5.7 5 0 9.9-.6 14.8-1.7 0 0 0 0 0 0 .1 0 .1 0 .1 0 0 .1 0 .1 0 .1.1 0 .1 0 .1.1v5.6s0 .1-.1.1c0 0 0 0 0 .1-1.6 1.1-3.7 1.7-5.6 2.3-.8.3-1.6.5-2.4.7-7.5 1.7-15.4 1.3-22.7-1.2-6.8-2.4-13.8-8.2-15.5-15.2-.9-3.8-1.6-7.6-1.9-11.5-.6-5.8-.6-11.7-.8-17.5C3.9 24.5 4 20 4.9 16 6.7 7.9 14.1 2.2 22.3 1c1.4-.2 4.1-1 16.5-1h.1C51.4 0 56.7.8 58.1 1c8.4 1.2 15.5 7.5 16.6 15.6Z" fill="currentColor"></path></svg> <div style="color: #787588; margin-top: 16px;">Post by @Edent@mastodon.social</div> <div style="font-weight: 500;">View on Mastodon</div> </a> </blockquote>

<script data-allowed-prefixes="https://mastodon.social/" async="" src="https://mastodon.social/embed.js"></script>

<h2 id="tldr"><a href="https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/#tldr">tl;dr</a></h2>

<p>You can <a href="https://gitlab.com/edent/swarmtosocial/-/blob/main/swarmtosocial.py">get the SwarmToSocial code from my GitLab</a>.</p>

<p>At the moment, developers get <a href="https://foursquare.com/pricing/">10,000 API calls for free each month</a>. That's probably more than enough for most personal uses.</p>

<h2 id="documentation"><a href="https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/#documentation">Documentation</a></h2>

<p>I was pleasantly surprised that <a href="https://docs.foursquare.com/developer/reference/create-a-checkin">FourSquare's CheckIn documentation</a> was fairly easy to use and understand.</p>

<p>Once you've <a href="https://foursquare.com/developers/home">signed up for a developer account</a> you can create an OAuth app. That will generate a Client ID (<code>ABC123</code>), Client Secret (<code>XYZ789</code>), and you supply a Project URL.</p>

<p>Once done you can <a href="https://docs.foursquare.com/developer/reference/personalization-apis-authentication">follow the Authentication documentation</a>. Or just visit:</p>

<pre><code class="language-_">https://foursquare.com/oauth2/authenticate?
   client_id=ABC123
  &amp;response_type=code
  &amp;redirect_uri=https://example.com/
</code></pre>

<p>Sign in with your FourSquare account. It will redirect you to:</p>

<p><code>https://example.com/?code=456QWE</code></p>

<p>Use that code to construct the final URl:</p>

<pre><code class="language-_">https://foursquare.com/oauth2/access_token?
   client_id=ABC123
  &amp;client_secret=XYZ789
  &amp;grant_type=authorization_code
  &amp;redirect_uri=http://example.com/
  &amp;code=456QWE
</code></pre>

<p>That will respond with the Access Token:</p>

<pre><code class="language-json">{
   "access_token":"asdfghjkl123456"
}
</code></pre>

<p>Hurrah! Posting a new checkin is <em>relatively</em> simple. POST to this URl with a header of <code>accept: application/json</code></p>

<pre><code class="language-_">https://api.foursquare.com/v2/checkins/add?
   v=20260223
  &amp;venueId=13600425
  &amp;shout=This%20is%20a%20test
  &amp;oauth_token=asdfghjkl123456
</code></pre>

<ul>
<li><code>v</code> is, rather confusingly, a date. <a href="https://docs.foursquare.com/developer/reference/versioning">The versioning documentation</a> has more details but, basically, set it to the date you deployed your app.</li>
<li><code>venuId</code> you'll need to find yourself (more on that later).</li>
<li><code>shout</code> is up to 140 characters (!) of URl encoded text.</li>
</ul>

<p>That will send back rather a lot of JSON. Here are the important bits:</p>

<pre><code class="language-json">{
  "meta": {
    "code": 200,
    "requestId": "123456789"
  },
  "response": {
    "checkin": {
      "id": "987654321",
      "createdAt": 1771843820,
      "type": "checkin",
      "visibility": "closeFriends",
      "shout": "This is a test of the API",
      "timeZoneOffset": -300,
      "editableUntil": 1771930220000,
      "user": {
        "id": "56367",
        "firstName": "Terence",
        "lastName": "Eden",
        "relationship": "self",
        "displayName": "Terence Eden"
      },
      "venue": {
        "id": "QWERTYUIOP",
        "name": "My Birthday Party!",
        "contact": {},
        "location": {
          "isFuzzed": true,
          "lat": 39.123456789,
          "lng": -84.987654321,
          "cc": "US",
          "city": "Cincinnati",
          "state": "KY",
          "country": "United States",
          "formattedAddress": [
            "Cincinnati, KY",
            "United States"
          ]
        }
      },
      "checkinShortUrl": "https://swarmapp.com/user/56367/checkin/987654321?s=wRZ7ByNfCW1DNrOIpsRcytPZelE"
    }
  }
}
</code></pre>

<p>For my purposes, the <code>shout</code> and <code>checkinShortUrl</code> are the most important. You can view a sample check in:</p>

<p><a href="https://swarmapp.com/user/56367/checkin/699c34b55bad6b7fb1695544?s=LA7jCaAtH-s9CwSpgQrQdHrP5-8">https://swarmapp.com/user/56367/checkin/699c34b55bad6b7fb1695544?s=LA7jCaAtH-s9CwSpgQrQdHrP5-8</a></p>

<h2 id="venue-id"><a href="https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/#venue-id">Venue ID</a></h2>

<p>If you're already using <a href="https://shkspr.mobi/blog/2018/11/extracting-your-data-from-untappd/">a service like Untappd</a> you might be able to get the venue ID from that.</p>

<p>If not, FourSquare provides <a href="https://opensource.foursquare.com/os-places/">100 million points of interest</a> for free - although with <a href="https://community.openstreetmap.org/t/foursquare-releases-100m-poi-dataset-under-apache-2-0/121883">questionable data quality</a>.</p>

<p>Alternatively, you can <a href="https://docs.foursquare.com/fsq-developers-places/reference/place-search">search by location</a>:</p>

<pre><code class="language-_">curl --request GET \
     --url 'https://places-api.foursquare.com/places/search?ll=51.123%2C0.123&amp;radius=1000&amp;sort=POPULARITY' \
     --header 'X-Places-Api-Version: 2025-06-17' \
     --header 'accept: application/json' \
     --header 'authorization: Bearer ABC123'
</code></pre>

<p>As far as I can see, the <code>Bearer Token</code> only exists <a href="https://docs.foursquare.com/fsq-developers-places/reference/place-search">on the documentation page</a>. I couldn't find it in my developer console. Weird!</p>

<p>That gets you back:</p>

<pre><code class="language-json">{
  "results": [
    {
      "fsq_place_id": "4be584ed2457a593ad8cab15",
      "latitude": 51.11783041264215,
      "longitude": 0.11219274871133413,
      "categories": [
        {
          "fsq_category_id": "4bf58dd8d48988d1fa941735",
          "name": "Farmers Market",
          "short_name": "Farmers Market",
          "plural_name": "Farmers Markets",
          "icon": {
            "prefix": "https://ss3.4sqi.net/img/categories_v2/shops/food_farmersmarket_",
            "suffix": ".png"
          }
        }
      ],
      "date_created": "2010-05-08",
      "date_refreshed": "2025-11-01",
      "distance": 970,
      "extended_location": {},
      "link": "/places/4be584ed2457a593ad8cab15",
      "location": {
        "address": "",
        "locality": "Hartfield",
        "region": "East Sussex",
        "postcode": "",
        "admin_region": "England",
        "country": "GB",
        "formatted_address": "Hartfield, East Sussex"
      },
      "name": "Perryhill Farm Shop",
      "placemaker_url": "https://foursquare.com/placemakers/review-place/4be584ed2457a593ad8cab15",
      "related_places": {},
      "social_media": {
        "twitter": ""
      },
      "tel": "",
      "website": "http://www.perryhillorchards.co.uk/index.php?sec=4"
    },
    {
      "fsq_place_id": "8896f77565e54a658585301d",
      "latitude": 51.11649,
      "longitude": 0.13131,
      "categories": [],
      "date_created": "2021-12-06",
      "date_refreshed": "2021-12-06",
      "distance": 909,
      "extended_location": {},
      "link": "/places/8896f77565e54a658585301d",
      "location": {
        "address": "Priory Park, Beech Green Lane",
        "locality": "Withyham",
        "region": "East Sussex",
        "postcode": "TN7 4DB",
        "admin_region": "England",
        "post_town": "Hartfield",
        "country": "GB",
        "formatted_address": "Priory Park, Beech Green Lane, Withyham, East Sussex, TN7 4DB"
      },
      "name": "Spectra Studios",
      "placemaker_url": "https://foursquare.com/placemakers/review-place/8896f77565e54a658585301d",
      "related_places": {},
      "social_media": {},
      "tel": "01892 487149"
    },
  ],
  "context": {
    "geo_bounds": {
      "circle": {
        "center": {
          "latitude": 51.123,
          "longitude": 0.1234
        },
        "radius": 1000
      }
    }
  }
}
</code></pre>

<p>You can manually check a place using the Placemaker site: <a href="https://foursquare.com/placemakers/review-place/64eca80f0398c97ab52298ec">https://foursquare.com/placemakers/review-place/64eca80f0398c97ab52298ec</a></p>

<h2 id="getting-existing-checkins"><a href="https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/#getting-existing-checkins">Getting Existing Checkins</a></h2>

<p>What if you've checked in to a place using the official Swarm app? How do you get your own recent checkin data?</p>

<p>Again, there is <a href="https://docs.foursquare.com/developer/reference/get-user-checkins">documentation on getting user checkins</a>.</p>

<pre><code class="language-bash">curl --request GET \
     --url 'https://api.foursquare.com/v2/users/self/checkins?v=20260223&amp;limit=2&amp;offset=0&amp;oauth_token=asdfghjkl123456' \
     --header 'accept: application/json'
</code></pre>

<p>Where it says <code>oauth_token</code> it <em>actually</em> means the <code>access_token</code>.</p>

<p>The JSON that is returned is a bit verbose, so I've simplified it here:</p>

<pre><code class="language-json">{
  "meta": {
    "code": 200,
    "requestId": "699c6505b488565a31e315e3"
  },
  "response": {
    "checkins": {
      "count": 2344,
      "items": [
        {
          "id": "699c34b55bad6b7fb1695544",
          "createdAt": 1771844789,
          "type": "checkin",
          "visibility": "closeFriends",
          "entities": [],
          "shout": "Testing the API using an Untappd FourSquare ID.",
          "timeZoneOffset": 0,
          "editableUntil": 1771931189000,
          "venue": {
            "id": "64eca80f0398c97ab52298ec",
            "name": "Abbey Wood Fossil Pit",
            "contact": {},
            "location": {
              "lat": 51.487514,
              "lng": 0.13048041,
              "postalCode": "SE2 0AX",
              "cc": "GB",
              "country": "United Kingdom",
              "formattedAddress": [
                "SE2 0AX"
              ]
            },
            "createdAt": 1693231119
          },
        },
</code></pre>

<p>Annoyingly, there's no <code>checkinShortUrl</code> which means it can't easily be shared.</p>

<p>For that, you'll need to <a href="https://docs.foursquare.com/developer/reference/get-checkin-details">use the <code>get-checkin-details</code> API</a>:</p>

<pre><code class="language-bash">curl --request GET \
     --url 'https://api.foursquare.com/v2/checkins/699c34b55bad6b7fb1695544?v=20250202&amp;oauth_token=asdfghjkl123456' \
     --header 'accept: application/json'
</code></pre>

<p>Which will return this (truncated for brevity):</p>

<pre><code class="language-json">{
  "meta": {
    "code": 200,
    "requestId": "699c67de5f5c0a0e8ab234db"
  },
  "response": {
    "checkin": {
      "id": "699c34b55bad6b7fb1695544",
      "createdAt": 1771844789,
      "type": "checkin",
      "shout": "Testing the API using an Untappd FourSquare ID.",
      "timeZoneOffset": 0,
      "checkinShortUrl": "https://swarmapp.com/user/56367/checkin/699c34b55bad6b7fb1695544?s=LA7jCaAtH-s9CwSpgQrQdHrP5-8",
</code></pre>

<h2 id="photos"><a href="https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/#photos">Photos</a></h2>

<p>If there's a photo with the checkin, it will be return in the JSON like this:</p>

<pre><code class="language-json">{
  "response": {
    "checkin": {
      "photos": {
        "count": 1,
        "items": [
          {
            "id": "699f3a9f96799c05c0f16c9c",
            "createdAt": 1772042911,
            "prefix": "https://fastly.4sqi.net/img/general/",
            "suffix": "/56367_5VYox4Y-hs66wURVsYc1NLgOokfwBfcWhtKQrOlMdD8.jpg",
            "width": 1008,
            "height": 1344,
</code></pre>

<p>The URl for the image is <code>prefix width x height suffix</code> - in this case <a href="https://fastly.4sqi.net/img/general/1008x1344/56367_5VYox4Y-hs66wURVsYc1NLgOokfwBfcWhtKQrOlMdD8.jpg">https://fastly.4sqi.net/img/general/1008x1344/56367_5VYox4Y-hs66wURVsYc1NLgOokfwBfcWhtKQrOlMdD8.jpg</a></p>

<p>You can adjust the width and height if you want a thumbnail or some other resolution.</p>

<p>If there's no photo, the count will be 0.</p>

<h2 id="putting-it-all-together"><a href="https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/#putting-it-all-together">Putting it all together</a></h2>

<p>Every 15 minutes, <a href="https://gitlab.com/edent/swarmtosocial/-/blob/main/swarmtosocial.py">the SwarmToSocial code</a> does the following:</p>

<ol>
<li>Get the most recent checkin.</li>
<li>Read a local file to get the previously seen checkin ID.</li>
<li>If the checkin ID hasn't been seen before:

<ol>
<li>Get the checkin details.</li>
<li>Get the photo if it exists</li>
<li>Post the checkin (plus photo) to Mastodon &amp; BlueSky.</li>
<li>Save the checkin ID to a file.</li>
</ol></li>
</ol>

<p>Enjoy!</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=68230&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/06/using-foursquares-api-to-post-location-checkins-to-social-media/feed/</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Why is it so hard to passively stalk my friends' locations?]]></title>
		<link>https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/</link>
					<comments>https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Wed, 15 Apr 2026 11:34:45 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[FourSquare]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[location]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=68114</guid>

					<description><![CDATA[I feel terribly guilty when I visit a new city, post photos of my travels, only to have a friend say &#34;Hey! Why didn&#039;t you let me know you were in my neck of the woods?&#34;  Similarly, if I bump into an old acquaintance at a conference, we both tend to say &#34;If only I&#039;d known you were here, we could have had dinner together last night!&#34;  I do enjoy the serendipity of events like FOSDEM - randomly…]]></description>
										<content:encoded><![CDATA[<p>I feel terribly guilty when I visit a new city, post photos of my travels, only to have a friend say "Hey! Why didn't you let me know you were in my neck of the woods?"</p>

<p>Similarly, if I bump into an old acquaintance at a conference, we both tend to say "If only I'd known you were here, we could have had dinner together last night!"</p>

<p>I do enjoy the serendipity of events like FOSDEM - randomly seeing a mate and expressing the joy of spontaneity. But I also like arranging to meet up in advance.</p>

<p>At the moment, my strategy is sending a blast on social media saying "I'm visiting [this city] next week, anyone fancy a beer and a natter?" I've met friends all over Europe, Australia, and New Zealand that way.  <a href="https://shkspr.mobi/blog/2025/06/meeting-my-fedifriends-afk/">It mostly works</a>.  But I can't help feeling it is inefficient and prone to missing connections.</p>

<p>I even wrote my own code to auto-post FourSquare checkins to my other social media sites.</p>

<p>Here are my ideal scenarios. Imagine something built in to Signal / WhatsApp / Whatever app you already use.</p>

<h2 id="plan-in-advance"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#plan-in-advance">Plan In Advance</a></h2>

<p>I tell my app that I'm going to Barcelona from 14th - 19th February and am happy to meet any of my friends.</p>

<p><em>✨Background Magic✨</em></p>

<p>My friend Alice has also planned a trip to Barcelona around those dates. She gets a ping saying that one of her friends is going to be in the same city. Does she want to know more?</p>

<p>So far, so <a href="https://en.wikipedia.org/wiki/Dopplr">Dopplr</a>.</p>

<p>My friend Bob lives just outside of Barcelona. He's set his "willing to travel" settings to be about 30 minutes, so also receives a ping.</p>

<p>I don't know that either of them have seen the notification until they decide they want to meet.</p>

<h2 id="spontaneous-fun"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#spontaneous-fun">Spontaneous Fun</a></h2>

<p>I step off the train in Manchester, England England.  Perhaps the app notices I'm away from home, or maybe I press the "Anyone Around?" button.</p>

<p>On a map I can see friends who have shared their rough location. I decide to message Chuck to see if he's free for a chat.</p>

<p>Dave notices my location is now within his preferred travel distance. He gives me a ring.</p>

<p>A bit like how FourSquare used to be - but with less precision.</p>

<h2 id="downsides"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#downsides">Downsides</a></h2>

<p>The above is very much the "happy path". It doesn't look at any of the knotty problems or grapple with the UI that would be needed to make this work.  But we know the technology for sharing location is viable - so what are the social issues that make this so difficult?</p>

<h3 id="social-awkwardness"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#social-awkwardness">Social Awkwardness</a></h3>

<p>"Oh, fuck, Edgar's location says he's in town. Can we pretend to be out of the country?"</p>

<p>Alternatively, "Huh, I know at least a dozen people who live in Skegness. Why aren't any of them responding to me?"</p>

<p>Social pressure and awkwardness are hard problems. No one wants to use the app that makes you feel like a friendless loser.</p>

<h3 id="privacy"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#privacy">Privacy</a></h3>

<p>Do you <em>want</em> your friends knowing your every movement? I'm sure some people do, but most probably don't. It's possible to sketch out some vague controls:</p>

<ul>
<li>Only send a notification if I push this button.</li>
<li>Don't send alerts if I am within this radius of my home / work.</li>
<li>Fuzz my location to the city / state / country level.</li>
</ul>

<h3 id="danger"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#danger">Danger</a></h3>

<p>Is it a risk to let people know vaguely where you are? Is meeting up with (semi-) strangers from the Internet a smart life choice? Is having an app stalk you across the globe giving too much data to advertisers?</p>

<p>Does that creep from work abuse the system to keep popping up whenever you're out with friends?</p>

<h2 id="technology"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#technology">Technology</a></h2>

<p>I said the technology exists for this, and that was sort of true. Every device has GPS &amp; an Internet connection. Storing a log of friends and sending them a message is a solved problem.</p>

<p>But is it solved in a decentralised and privacy preserving way?</p>

<p>No one wants to give all this power to one company. Google will build it and kill it. Facebook will sell your secrets to dropshippers. A funky start-up will be acquhired by Apple &amp; restricted to iOS devices.</p>

<p>My location is fuzzed to an acceptable degree of imprecision and then sent… where? To all my friends directly? To a central server? Can <a href="https://en.wikipedia.org/wiki/K-anonymity"><em>k</em>-anonymity</a> help?</p>

<p>Is this a separate app? Everyone seemed to leave FourSquare after they buggered around with it. Perhaps it is just a feature in existing apps?</p>

<h3 id="whats-already-there"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#whats-already-there">What's Already There?</a></h3>

<p>Messaging apps like Signal, Telegram, and WhatsApp allow you to share your location with one or more friends.</p>

<p>To me, it feels a bit weird to manually send a dropped pin to some / all of my contact. It also doesn't let you share "tomorrow I will be in…"</p>

<p>Using "Stories" is the common way to share an update with all contacts - but none of them let you automatically share your location in a story.</p>

<p>FourSquare's Swarm app allows you to check in to a "neighbourhood". But there's no obvious way of saying "London" or "Manchester" - and I'm not sure how close to an area you need to be to get an alert that your friend is there.</p>

<h2 id="whats-next"><a href="https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/#whats-next">What's Next?</a></h2>

<p>I don't want to build this. Trying to get everyone I know to adopt a new app isn't going to happen. With the fragmentation of messaging and the lack of interoperability, this is likely to remain an unsolved problem for some time.</p>

<p>So here's my strategy.</p>

<ul>
<li>Get back in to using FourSquare. Most of my friends seemed to stop using it back in 2017 when it was split into Swarm. But a few are still on there.</li>
<li>Manually post a story on Mastodon, BlueSky, Facebook, WhatsApp, Signal, and Telegram saying "Visiting Hamburg next week. Anyone want a beer?"</li>
<li>Hope that something better comes along.</li>
</ul>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=68114&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2026/04/why-is-it-so-hard-to-passively-stalk-my-friends-locations/feed/</wfw:commentRss>
			<slash:comments>4</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Rebuilding FourSquare for ActivityPub using OpenStreetMap]]></title>
		<link>https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/</link>
					<comments>https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#comments</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Sat, 27 Jan 2024 12:34:55 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[ActivityPub]]></category>
		<category><![CDATA[fediverse]]></category>
		<category><![CDATA[FourSquare]]></category>
		<category><![CDATA[geolocation]]></category>
		<category><![CDATA[OpenStreetMap]]></category>
		<guid isPermaLink="false">https://shkspr.mobi/blog/?p=49432</guid>

					<description><![CDATA[I used to like the original FourSquare. The &#34;mayor&#34; stuff was a bit silly, and my friends never left that many reviews, but I loved being able to signal to my friends &#34;I am at this cool museum&#34; or &#34;We&#039;re at this pub if you want to meet&#34; or &#34;Spending the day at the park&#34;.  So, is there a way to recreate that early Web 2.0 experience with open data and ActivityPub? Let&#039;s find out!  This quest is…]]></description>
										<content:encoded><![CDATA[<p>I used to like the original FourSquare. The "mayor" stuff was a bit silly, and my friends never left that many reviews, but I loved being able to signal to my friends "I am at this cool museum" or "We're at this pub if you want to meet" or "Spending the day at the park".</p>

<p>So, is there a way to recreate that early Web 2.0 experience with open data and ActivityPub? Let's find out!</p>

<p>This quest is divided into two parts.</p>

<ol>
<li>Get nearby "Points of Interest" (POI) from OpenStreetMap.</li>
<li>Share a location on the Fediverse.</li>
</ol>

<h2 id="openstreetmap-api"><a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#openstreetmap-api">OpenStreetMap API</a></h2>

<p>OpenStreetMap is the Wikipedia of maps. It is a freely available resource which anyone can edit (if they're skilled enough).</p>

<p>It also comes with a pretty decent API for querying things.  For example, <a href="https://overpass-turbo.eu/s/1GaE"><code>nw["amenity"]({{bbox}});</code> finds all "amenities" near a specific location</a>.</p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/01/amenities-fs8.png" alt="Map of a part of London. Some parts are highlighted." width="826" height="504" class="aligncenter size-full wp-image-49441">

<p>As you can see, it has highlighted <em>some</em> useful areas - a pharmacy and a pub. But it has <em>ignored</em> other useful locations - the train station and the park. It has also included some things that we may not want - bike parking and a taxi rank.</p>

<p>What API call is needed to get <em>useful</em> locations of of OverPass?</p>

<p>It's possible to specify the <em>type</em> of thing to find using <code>nw["amenity"="restaurant"];</code> - but adding every single type of thing would quickly end up with a very large query containing hundreds of types.</p>

<p>It is also possible to <em>exclude</em> specific types of places. This retrieves all amenities except for fast food joints:</p>

<pre><code class="language-_">nw["amenity"]({{bbox}});
-
nw["amenity"="fast_food"]({{bbox}});
</code></pre>

<p>Again, that would be complex.</p>

<p>Perhaps one solution is just to return <em>everything</em> and let the user decide if they want to check in to a telephone kiosk or a fire hydrant?  That's a bit user-hostile.</p>

<p>Instead, <a href="https://overpass-turbo.eu/s/1GaP">this query returns everything which has a name</a> <code>nw["name"]({{bbox}});</code></p>

<img src="https://shkspr.mobi/blog/wp-content/uploads/2024/01/names-fs8.png" alt="Map of London with several bits highlighted." width="826" height="504" class="aligncenter size-full wp-image-49442">

<p>That cuts out any unnamed things - like park benches and car-sharing spots. But it does add named roads and train lines.</p>

<p>It is possible to <a href="https://osm-queries.ldodds.com/tutorial/05-nodes-combined-filter.osm.html">use filters to exclude results from OverPass</a>. The best that I can come up with is: <a href="https://overpass-turbo.eu/s/1GaR"><code>nw["name"][!"highway"][!"railway"][!"waterway"][!"power"]({{bbox}});</code></a></p>

<p>That gets everything which has a name, but isn't a highway or railway or waterway or powerline. It isn't perfect - but it will do!</p>

<p>This is the query which will retrieve the 25 nearest things within 100 metres of a specific latitude and longitude. It includes the name and any other tags, the location, and the OSM ID.</p>

<p><a href="https://overpass-api.de/api/interpreter?data=[out:json];nw%5B%22name%22%5D%5B%21%22highway%22%5D%5B%21%22railway%22%5D%5B%21%22waterway%22%5D%5B%21%22power%22%5D(around:100,51.5202,-0.1040);out%20center%20qt%2025;"><code>overpass-api.de/api/interpreter?data=[out:json];nw["name"][!"highway"][!"railway"][!"waterway"][!"power"](around:100,51.5202,-0.1040);out center qt 25;</code></a></p>

<h2 id="activitypub"><a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#activitypub">ActivityPub</a></h2>

<p>There's good news and bad news here. Firstly, ActivityStreams (which are subscribed to in ActivityPub) <a href="https://www.w3.org/TR/activitystreams-vocabulary/#places">supports the concept of "Place"</a> and <a href="https://www.w3.org/ns/activitystreams#location">"Location"</a>.</p>

<p>Once the user has a latitude and longitude, the can share it - along with a message, photo, or anything else.</p>

<p>Something like:</p>

<pre><code class="language-json">{
    "@context": "https://www.w3.org/ns/activitystreams",
    "type": "Note",
    "content": "Here in NYC! &lt;a href=\"https://www.openstreetmap.org/way/958999496\"&gt;John Lennon's Imagine Mosaic&lt;/a&gt;.",
    "attachment": [
        {
            "type": "Image",
            "mediaType": "image\/jpeg",
            "url": "https:\/\/fastly.4sqi.net\/img\/general\/590x786\/56367_9pxuZJD7d1hgPdaMFcFq1pipvTTMynBJsYcpHH-b8mU.jpg",
            "name": "A photo of a mosaic which says 'Imagine'."
        }
    ],
    "location": {
        "name": "John Lennon's Imagine",
        "type": "Place",
        "longitude": 40.77563,
        "latitude": -73.97474
    }
}
</code></pre>

<p>For example, here's <a href="https://pixelfed.social/p/dansup/75783646421848064?fs=1">a PixelFed post with an attached location</a> - and this is the <a href="https://pixelfed.social/p/dansup/75783646421848064.json">JSON representation</a>. That status can be reposted into other social networks.</p>

<p>It is worth noting that Mastodon doesn't (natively) support location - if you <a href="https://mastodon.social/@Edent/100581317169164202">view my repost of that PixelFed post</a> you'll see there's no location metadata attached. That's OK! It just means that the status needs to include human-readable data.</p>

<p>Similarly, Mastodon doesn't support the <a href="https://www.w3.org/TR/activitystreams-vocabulary/#dfn-arrive"><code>arrive</code></a> vocabulary. So this will be limited to a message with a location attached.</p>

<p><a href="https://benjojo.co.uk/u/benjojo/h/5j4yjq8JbrKY6Vp79K">Other ActivityPub services <em>do</em> support location</a>.</p>

<h2 id="putting-it-all-together"><a href="https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/#putting-it-all-together">Putting it all together</a></h2>

<p>Well… that's a job for next week. Probably!</p>

<ul>
<li>Building a web site which gets the user's location is easy.</li>
<li>Getting the data from OverPass should be straightforward.</li>
<li>Creating an ActivityPub server which can post geotagged notes into the Fediverse might be a little beyond my skillset!  Some testing with <a href="https://tinysubversions.com/notes/activitypub-tool/">Darius Kazemi's AP Glitch</a> suggests this should work.</li>
</ul>

<p>If you'd like to help, please leave a comment.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=49432&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2024/01/rebuilding-foursquare-for-activitypub-using-openstreetmap/feed/</wfw:commentRss>
			<slash:comments>30</slash:comments>
		
		
			</item>
		<item>
		<title><![CDATA[Getting Images from a Foursquare Checkin]]></title>
		<link>https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/</link>
					<comments>https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/#respond</comments>
				<dc:creator><![CDATA[@edent]]></dc:creator>
		<pubDate>Tue, 15 Mar 2011 12:35:22 +0000</pubDate>
				<category><![CDATA[/etc/]]></category>
		<category><![CDATA[4SQ]]></category>
		<category><![CDATA[api]]></category>
		<category><![CDATA[bit.ly]]></category>
		<category><![CDATA[dabr]]></category>
		<category><![CDATA[FourSquare]]></category>
		<category><![CDATA[programming]]></category>
		<guid isPermaLink="false">http://shkspr.mobi/blog/?p=3749</guid>

					<description><![CDATA[&#34;Oi!&#34; shouted Whatleydude, &#34;Get Dabr to show images from foursquare checkins!&#34; &#34;Righty-ho sir!&#34; I said.  I started coding furiously.  Of course, things are never quite as simple as I first thought....  So, how do we go from http://4sq.com/fgIWov to   1 Expand the URL  Get your Bit.ly API Key.  http://api.bitly.com/v3/expand    ?shortUrl=http://4sq.com/fgIWov    &#38;login=YOUR_BIT_LY_USERNAME   …]]></description>
										<content:encoded><![CDATA[<p>"Oi!" shouted <a href="http://whatleydude.com/">Whatleydude</a>, "Get Dabr to show images from foursquare checkins!"
"Righty-ho sir!" I said.  I started coding furiously.  Of course, things are never quite as simple as I first thought....</p>

<p>So, how do we go from <a href="http://4sq.com/fgIWov">http://4sq.com/fgIWov</a>
to
<img src="https://shkspr.mobi/blog/wp-content/uploads/2011/03/4sq.jpg" alt="Photo of a MacBook. The decal sticker is of Iron Man. His hand blaster is replaced with the Apple logo." width="236" height="329" class="aligncenter size-full wp-image-47785"></p>

<h2 id="1-expand-the-url"><a href="https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/#1-expand-the-url">1 Expand the URL</a></h2>

<p>Get your <a href="http://bit.ly/a/your_api_key/">Bit.ly API Key</a>.</p>

<pre>http://api.bitly.com/v3/expand
   ?shortUrl=http://4sq.com/fgIWov
   &amp;login=YOUR_BIT_LY_USERNAME
   &amp;apiKey=YOUR_BIT_LY_API_KEY
   &amp;format=txt
</pre>

<p>You can, if you prefer, get the info back in JSON or XML. See the <a href="http://code.google.com/p/bitly-api/wiki/ApiDocumentation#/v3/expand">Bit.ly API Expand Documentation</a>.</p>

<h2 id="2-the-foursquare-url"><a href="https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/#2-the-foursquare-url">2 The Foursquare URL</a></h2>

<p>Bit.ly gives us the URL which includes a checkin ID and a signature.</p>

<pre>http://foursquare.com/edent/checkin/4d7e5b4f9df3f04d7400c394
   ?s=afH3jJg3L9HpLqVIwiqp-YpNL5k
</pre>

<h2 id="3-the-foursquare-api-call"><a href="https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/#3-the-foursquare-api-call">3 The Foursquare API Call</a></h2>

<p>From this, we construct an API call to Foursquare.  It looks like this</p>

<pre>https://api.foursquare.com/v2/checkins/4d7e5b4f9df3f04d7400c394
   ?signature=afH3jJg3L9HpLqVIwiqp-YpNL5k
   &amp;oauth_token=YOUR_FOURSQUARE_OAUTH_TOKEN
</pre>

<p><strong>NOTE</strong> the Foursquare URL says <em>?s=</em> whereas the API call requires <em>?signature=</em>
That caused me more frustration than it should...</p>

<p>Getting your OAuth token is a little cumbersome - follow the steps at the <a href="http://developer.foursquare.com/docs/oauth.html">Foursquare Developers site</a>.</p>

<h2 id="4-the-json-response"><a href="https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/#4-the-json-response">4 The JSON Response</a></h2>

<p>I'll cut the cruft out of here, so you only see what you need to display images.
The response gives us the link to the full sized image - as well as several different sizes should you wish to display thumbnails.</p>

<pre lang="json">{
  "meta":{
     "code":200
  },
  "response":{
     "checkin":{
        },
   ...
   "photos":{
      "count":1,
      "items":[
         {
            "id":"4d7e5b4f9df3f04d7400c394",
            "createdAt":1300184596,
            "url":"http://playfoursquare.s3.amazonaws.com/pix/A.jpg",
            "sizes":{
               "count":4,
               "items":[
                  {
                     "url":"http://playfoursquare.s3.amazonaws.com/pix/B.jpg",
                     "width":720,"height":540
                  },
                  {
                     "url":"http://playfoursquare.s3.amazonaws.com/derived_pix/C.jpg",
                     "width":300,"height":300
                  },
                  {
                     "url":"http://playfoursquare.s3.amazonaws.com/derived_pix/D.jpg",
                     "width":100,"height":100
                  },
                  {
                     "url":"http://playfoursquare.s3.amazonaws.com/derived_pix/E.jpg",
                     "width":36,
                     "height":36
                  }
               ]
            },
</pre>

<h2 id="5-gotchas"><a href="https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/#5-gotchas">5 Gotchas</a></h2>

<p>Again, nothing in life is easy.  Not all foursquare checkins have an image associated with them.  In which case, we can do one of three things.</p>

<ol>
    <li>Display no image</li>
    <li>Display the user's avatar - if it's public</li>
    <li>Display an image of the venue - or one of the icons associated with it.</li>
</ol>

<h2 id="6-embed-ly"><a href="https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/#6-embed-ly">6 Embed.ly</a></h2>

<p>Dabr uses the <a href="http://embed.ly/">awesome Embedly service</a>.  Rather than having hundreds of different regular expressions and API calls to get embedded images, Embed.ly gives us a single end point to call.
So, rather than rewrite huge chunks of Dabr's code, I've submitted this to the clever-clogs behind Embed.ly - that way, everyone can benefit.</p>

<p>Any questions, comments, or clarifications - please let me know in the comments box.</p>
<img src="https://shkspr.mobi/blog/wp-content/themes/edent-wordpress-theme/info/okgo.php?ID=3749&HTTP_REFERER=RSS" alt="" width="1" height="1" loading="eager">]]></content:encoded>
					
					<wfw:commentRss>https://shkspr.mobi/blog/2011/03/getting-images-from-a-foursquare-checkin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
