# What API episode action causes AntennaPod to consider an episode as heard?

**URL:** https://forum.antennapod.org/t/what-api-episode-action-causes-antennapod-to-consider-an-episode-as-heard/9178
**Category:** Support
**Created:** [September 13, 2026, 5:53pm UTC](https://forum.antennapod.org/t/what-api-episode-action-causes-antennapod-to-consider-an-episode-as-heard/9178 "2026-09-13T17:53:10Z")
**Posts on this page:** 3
**Page:** 1

<div class="post-metadata">

### Author: ![scfc](https://forum.antennapod.org/user_avatar/forum.antennapod.org/scfc/32/5192_2.png) [@scfc](https://forum.antennapod.org/u/scfc)
#### Post date: [September 13, 2026, 5:53pm UTC](https://forum.antennapod.org/t/what-api-episode-action-causes-antennapod-to-consider-an-episode-as-heard/9178/1 "2026-09-13T17:53:10Z")

</div>

**App version** : 3.12.2 (1a7d207c0) from Google Play

**Android version** : 12

**Device model** : Samsung Galaxy A12

I’m trying to synchronize my homebrew desktop podcast player with AntennaPod via [gpodder.net](http://gpodder.net) and mygpoclient 1.10 (Fedora 43).

My first try went as follows:

1. I sent an episode action to [gpodder.net](http://gpodder.net) from my desktop (device ID “scfc\_client”) that an episode had been played.
2. I updated my feeds on AntennaPod (device ID “AP”) so that the episode was known to AntennaPod.
3. I synchronized on AntennaPod so that the episode action was known to AntennaPod.
4. However, the episode was not marked as played on AntennaPod.
5. I marked the episode as played on AntennaPod.
6. I synchronized on AntennaPod.
7. I downloaded the list of episode actions from [gpodder.net](http://gpodder.net).

The example result for one episode is as follows:

```auto
$ jq '.[] | select(.podcast == "https://feeds.acast.com/public/shows/678fe5b2fc105e4d36aef8ed" and .episode == "https://sphinx.acast.com/p/open/s/678fe5b2fc105e4d36aef8ed/e/6aa16003647921ed602d6f89/media.mp3")' /tmp/a.json
{
  "podcast": "https://feeds.acast.com/public/shows/678fe5b2fc105e4d36aef8ed",
  "episode": "https://sphinx.acast.com/p/open/s/678fe5b2fc105e4d36aef8ed/e/6aa16003647921ed602d6f89/media.mp3",
  "action": "play",
  "device": "scfc_client",
  "timestamp": "2026-09-11T00:06:12"
}
{
  "podcast": "https://feeds.acast.com/public/shows/678fe5b2fc105e4d36aef8ed",
  "episode": "https://sphinx.acast.com/p/open/s/678fe5b2fc105e4d36aef8ed/e/6aa16003647921ed602d6f89/media.mp3",
  "action": "play",
  "device": "AP",
  "timestamp": "2026-09-13T03:18:49",
  "started": 3404,
  "position": 3404,
  "total": 3404
}
$

```

(This shows that the podcast and episode URLs are identical for both episode actions, so this is not a case of different feed URLs or something similar.)

I haven’t read AntennaPod’s code yet, so could someone please summarize how AntennaPod determines if an episode action should cause an episode to be marked as heard? Do I need to set started/position/total? Do these values need to correspond to AntennaPod’s notion of the episode length, or could I pass 100/100/100 as well?

Another idea would be not to set up my homebrew desktop player as a [gpodder.net](http://gpodder.net) client, but as a server instead to which AntennaPod would connect directly. Would I need to implement the complete API for AntennaPod to work, or does AntennaPod only use a subset? Would AntennaPod fail gracefully if a particular API is not implemented (yet), or could that cause data loss?

(I do acknowledge that resetting AntennaPod in case of an error would not be the end of the world as it is “just” a podcast player, but it works perfectly fine without any hassle (thanks!), so I’d like to keep it that way. :-))

---

<div class="post-metadata">

### Author: ![scfc](https://forum.antennapod.org/user_avatar/forum.antennapod.org/scfc/32/5192_2.png) [@scfc](https://forum.antennapod.org/u/scfc)
#### Post date: [September 14, 2026, 7:22am UTC](https://forum.antennapod.org/t/what-api-episode-action-causes-antennapod-to-consider-an-episode-as-heard/9178/2 "2026-09-14T07:22:25Z")

</div>

Having looked at `net/sync/service/src/main/java/de/danoeh/antennapod/net/sync/service/SyncService.java`, it appears as if the logic is in processEpisodeActions():

```auto
            String guid = GuidValidator.isValidGuid(action.getGuid()) ? action.getGuid() : null;
            FeedItem feedItem = DBReader.getFeedItemByGuidOrEpisodeUrl(guid, action.getEpisode());
            if (feedItem == null) {
                Log.i(TAG, "Unknown feed item: " + action);
                continue;
            }
            if (feedItem.getMedia() == null) {
                Log.i(TAG, "Feed item has no media: " + action);
                continue;
            }
            FeedMedia media = feedItem.getMedia();
            media.setPosition(action.getPosition() * 1000);
            int smartMarkAsPlayedSecs = UserPreferences.getSmartMarkAsPlayedSecs();
            boolean almostEnded = media.getDuration() > 0
                    && media.getPosition() >= media.getDuration() - smartMarkAsPlayedSecs * 1000;
            if (almostEnded) {
                Log.d(TAG, "Marking as played: " + action);
                feedItem.setPlayed(true);
                media.setPosition(0);
                queueToBeRemoved.add(feedItem.getId());
            } else {
                Log.d(TAG, "Setting position: " + action);
            }
            updatedItems.add(feedItem);

```

So I set `started`, `position` and `total` to `10000` (~ three hours) which should always satisfy the condition, but the episode remained unheard. Do I have to add the episode to the queue first?

Is there a way to inspect AntennaPod’s log on a mobile device?

---

<div class="post-metadata">

### Author: ![ByteHamster](https://forum.antennapod.org/user_avatar/forum.antennapod.org/bytehamster/32/4041_2.png) [@ByteHamster](https://forum.antennapod.org/u/ByteHamster)
#### Post date: [September 14, 2026, 7:44am UTC](https://forum.antennapod.org/t/what-api-episode-action-causes-antennapod-to-consider-an-episode-as-heard/9178/3 "2026-09-14T07:44:44Z")

</div>

You can inspect the logs through adb

> [@scfc](#):
>
> set `started`, `position` and `total` to `10000` (~ three hours) which should always satisfy the condition

Don’t do that. Use the actual duration
