Skip to content
Back to skills

Google Photos Link

ASecurity

Download and view photos from a public Google Photos share link (photos.app.goo.gl or photos.google.com/share). Use whenever a Google Photos link should be viewed, described, or analyzed — web_fetch alone returns album metadata, never the pixels.

  • 11 stars
  • 0 votes
  • 0 copies
  • 6 views
  • Added September 9, 2026
developmentjavascriptpythongojavashellbashdebugging

Works with

  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned September 9, 2026

npx -y skills add agentydragon/ducktape --skill google_photos_link --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Google Photos Link?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Google Photos Link
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/agentydragon-google-photos-link/badge)](https://www.skillsdirectory.com/skills/agentydragon-google-photos-link)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: google-photos-link
description: >-
  Download and view photos from a public Google Photos share link
  (photos.app.goo.gl or photos.google.com/share). Use whenever a Google
  Photos link should be viewed, described, or analyzed — web_fetch alone
  returns album metadata, never the pixels.
---

# Google Photos Link Access

## Why this skill exists

A Google Photos share link does **not** point at an image file. It points at a
JavaScript web app. Calling `web_fetch` on the link returns only the album
_shell_: a title, an `og:image` thumbnail, and a list of links to per-photo
pages. It does **not** give you the photos themselves. To actually see the
images you have to walk one extra layer: open each photo's page, pull out its
direct `lh3.googleusercontent.com` image URL, and download the bytes. This skill
encodes that walk so you don't rediscover it each time.

## Quickest path: run the script

A tested helper does the whole walk. Prefer it over doing the steps by hand.

```bash
# Download viewable JPEGs (longest side 2048px) into ./photos/
python3 scripts/fetch_google_photos.py "<share_url>" --out ./photos --width 2048

# Then open them to actually look:
#   view ./photos/photo_01.jpg   (etc.)
```

Other modes:

```bash
python3 scripts/fetch_google_photos.py "<url>" --original   # full-resolution originals (=d)
python3 scripts/fetch_google_photos.py "<url>" --list       # print direct image URLs, download nothing
```

The script accepts the short link, the full album link, or a single-photo link.
After it finishes, **view the downloaded files** — downloading is not the goal;
seeing the images is. Report what you see.

## What the link looks like (so you can sanity-check it)

| Form         | Example                                                           |
| ------------ | ----------------------------------------------------------------- |
| Short        | `https://photos.app.goo.gl/C3aVzvb4GpZ9NA2e8`                     |
| Full album   | `https://photos.google.com/share/AF1Qip…?key=RDdF…`               |
| Single photo | `https://photos.google.com/share/AF1Qip…/photo/AF1Qip…?key=RDdF…` |

The `key=` token is the share credential. If a link has no `key`, it is almost
certainly a private album and this approach will hit a login wall.

## The manual method (for debugging, or if the script breaks)

Do this by hand only when the script fails and you need to see where.

1. **Resolve the link.** `web_fetch` (or `curl -sL`) the share URL. The short
   `goo.gl` link 302-redirects to `photos.google.com/share/<ALBUM_ID>?key=<KEY>`.
   The returned HTML's `og:title` tells you the album name and photo count.

2. **Collect photo IDs from the album page.** The album HTML contains one link
   per photo of the form `/share/<ALBUM_ID>/photo/<PHOTO_ID>?key=<KEY>`. Extract
   every `<PHOTO_ID>` and de-duplicate while preserving order:
   `grep -oE '/photo/[A-Za-z0-9_-]+'`. (The album page does **not** contain the
   full-size image URLs — only the cover, via `og:image`. That's why step 3 is
   needed.)

3. **Get each photo's direct image URL.** Fetch each photo page. Its HTML embeds
   the real image at a thumbnail size, e.g.
   `https://lh3.googleusercontent.com/pw/<TOKEN>=s250-k-no`. Grab the base
   (everything before `=`):
   `grep -oE 'https://lh3\.googleusercontent\.com/[A-Za-z0-9_/-]+' | head -1`.

4. **Resize via the URL suffix, then download.** The `lh3` base URL is a
   resizer. Append a suffix and `curl` the result:
   - `=w2048-no` → longest side 2048px, aspect preserved, **no upscaling**
     (`-no` is what prevents upscaling and extra server processing).
   - `=w1280-no`, `=s1024`, `=w1920-h1080` → other size options.
   - `=d` → the **original** bytes at full quality and native dimensions
     (also the only way to pull the original of a video).

5. **View the files.** Open each downloaded JPEG with the `view` tool and
   describe / analyze as asked.

## Picking a size

- **Just viewing / describing:** `--width 2048` is plenty and keeps files small.
- **Reading fine detail (text, engraving, small labels):** try `--width 4096`,
  or `--original` to avoid re-compression artifacts. Note `=w{N}-no` never
  upscales, so for an image whose original is smaller than N you'll get the
  original size regardless.
- **User wants the actual files to keep:** `--original`.

## Limitations — know these before promising results

- **Public link-shared albums only.** If the album was shared privately to
  specific Google accounts (no `key=` in the URL), the pages return a sign-in
  wall and there is nothing to scrape. Tell the user it needs to be shared "by
  link" / "anyone with the link."
- **Links can expire or be revoked.** The owner can turn off link sharing at any
  time, after which the same URL stops working.
- **Videos:** the per-photo page exposes a poster frame for stills easily; for
  video, `=d` on the base may fetch the original file, but playback/streaming
  variants are out of scope here.
- **Consent interstitials:** in some regions Google serves a cookie-consent page
  to non-browser clients. If the script returns 0 photos but the link works in a
  browser, fall back to `curl -sL` with a browser `User-Agent` and inspect the
  HTML, or have the user open it in an incognito window to confirm it's public.
- **Respect privacy.** Only fetch links the user has supplied or clearly wants
  opened. Don't enumerate or guess album/photo IDs.

## Troubleshooting

| Symptom                                        | Likely cause                                | Fix                                                                              |
| ---------------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------- |
| "no photos found"                              | private album / missing `key`               | Confirm the link opens in an incognito window; ask owner to share by link        |
| 0 photos but link works in browser             | consent page or odd UA handling             | Re-run with a browser `User-Agent`; inspect raw HTML for a redirect/consent form |
| Downloaded file isn't a valid image            | grabbed an interstitial, not the image      | Check `file <name>`; re-extract the `lh3` base from the photo page               |
| Image looks low-res                            | size suffix too small, or original is small | Use `--width 4096` or `--original`; remember `-no` won't upscale                 |
| `Syntax error: "(" unexpected` in a shell loop | script ran under `/bin/sh`, not bash        | Use the Python helper, or write bash to a file and run `bash file.sh`            |

Files in this skill

  • BUILD.bazel202 B
  • SKILL.md6.6 KB
  • scripts/fetch_google_photos.py6.4 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.