

# How to Turn Checked Job Rows Into an RSS Feed

Turn checked job rows into an RSS 2.0 feed. Give each item a stable ID and the original job link. Encode the text so the feed stays valid. Add a publication date only when the source time and time zone are known.

RSS means Really Simple Syndication. It is a document format that feed readers can open. RSS is written in XML. XML is structured text.

## Use rows you already checked

An Actor is a hosted data collection program. A Task is a saved input preset. The [Indeed Jobs Scraper](https://apify.com/chronometrica/indeed-jobs-scraper) Actor collects source rows. You write the RSS feed from rows you have already checked. The Actor does not write this feed for you.

Indeed is the job site these rows come from. Public build 1.2.14 can return the row fields this method uses. Those fields are jobId, jobUrl, title, companyName, descriptionText, descriptionStatus, postedAt, postedAtRaw, and scrapedAt. jobUrl is the job's web address. A web address is also called a URL.

A two-row control accepted source IDs, source URLs, and full description text. That control had a limit of two rows. A limited sample is not every job on the site. This method does not repair a blocked Upwork collection. It does not create a native Indeed feed.

The current public [Indeed Jobs Scraper Task](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-jobs-scraper) was checked at 2026-10-13T05:55:51.545104+00:00. Its saved input is:

```json
{"country": "us", "searchTerm": "software engineer", "maxItems": 100, "postedWithinDays": "7", "sortBy": "date", "maxPagesPerSearch": 2, "saveOnlyUniqueItems": true}
```

Use rows you already checked

Use that input only as a start. Replace the sample search term when you collect rows. Replace the country or location too. maxItems 100 limits the request. It is not a count of jobs found. maxPagesPerSearch 2 limits pages for one search. It is not full site coverage. saveOnlyUniqueItems true asks the collection to keep unique items. You still check IDs before you write the feed. A public Task does not mean the feed work is automatic.

Collection, row comparison, and [identity review](/en/articles/jobs-duplicates-reposts/) come before this method. Identity here means the stable source ID for one listing. Start only with a limited set of checked rows. Exclude a row when its ID is missing or still under review.

## Build the feed in eight steps

1. Define the sample and the purpose.

The purpose is to give a feed reader a valid document of checked listings. Start with useful rows that have known IDs. Keep a coverage receipt with them. A coverage receipt is a short note of which rows the feed includes. Exclude missing IDs. Exclude IDs that are still under review.

1. Choose the item identity rule.

For one Indeed listing, the key is the text indeed: immediately followed by jobId. A GUID is the item identity string. GUID means globally unique identifier. RSS stores it in the guid element. Set isPermaLink to false. False means the GUID is not a web address. Keep the same GUID when the title changes. Keep the same GUID when the description changes. A new source ID makes a new feed item under this rule. A new source ID is not proof of a new employer role. Use a separate method when you need to match the same role.

1. Create one RSS 2.0 feed with one channel.

The root element is rss, and its version is 2.0. Put one channel inside it. A channel is the main group that holds the feed title and its items. An item is one job listing in the feed. The [RSS specification](https://www.rssboard.org/rss-specification) requires a channel title, link, and description. Give all three. Add one item for each useful row. Give that item a title. Give it the original job web address as its link. Give it a short description. The same specification requires a title or a description on each item. This method gives both. Keep the channel link reachable for your reader.

1. Write the text with an XML serializer.

This writing step is called serialization. Do not paste raw job text into the file by hand. An ampersand or a less-than sign can break XML. The [XML syntax rules](https://www.w3.org/TR/xml/#syntax) show how those characters must be written. A serializer is a program that writes text in a safe XML form. It keeps Unicode letters, such as é. It keeps query text in a web address. A query string is the text after a question mark in a web address. Safe XML text is not safe HTML. HTML is the format used to draw a web page. A serializer does not make pasted HTML safe to display. Use a plain-text summary in the item. Keep the full job text at the original job page. Use descriptionText only when your row check accepted descriptionStatus. companyName can stay in the source row. The feed item does not require it.

1. Add pubDate only for a confirmed publication time.

pubDate is the RSS name for an item publication date. postedAt is optional, and it is parsed from source text. A filled postedAt value does not prove an exact publication time. Inspect postedAtRaw and the source evidence. Add pubDate only when the publication time and time zone are both confirmed. If the time is missing, relative, or uncertain, it stays unavailable. A relative time, such as a phrase that says a number of days ago, is not exact. The next date string is part of the original fictional sample. The [RSS date style](https://www.rssboard.org/rss-specification) looks like Mon, 12 Oct 2026 09:30:00 GMT. GMT is the zone name that this style uses for that UTC time. UTC means Coordinated Universal Time. When the publication time is unknown, leave pubDate out. You can name the observation time in the item summary or in the coverage receipt. scrapedAt is the observation time. Do not use scrapedAt as the publication time. lastBuildDate means the time the feed document was built. Add lastBuildDate only when you track that build time separately. Do not use the build time to fill an unknown job date.

1. Reconcile the source rows and the feed items.

Reconcile means to compare both lists and account for every row. Report the known IDs you used. Report rows you excluded. Report rows that are still unresolved. Report duplicate keys. Report the count of items with no publication date. A feed built from a limited dataset stays limited. A missing item in a later sample is not proof that the employer removed the job. How a reader deletes or updates items is a separate choice.

1. Parse the feed and check it in three layers.

Parse means to read the document and confirm its structure. First, parse the XML. Confirm the required elements. Confirm the item count. Confirm that each GUID is unique. Confirm that each GUID stays the same when text changes. Confirm each item link. Confirm that parsed text matches the source text. Confirm that each date keeps its source meaning. A successful parse shows that the XML structure is sound. It does not prove every RSS rule. It does not prove that every reader will accept the feed. Second, check the RSS rules you rely on. Third, test the reader you plan to use. Import means to open the feed in that reader. Import a small fictional feed before you add real rows. Keep that reader test separate from the document test.

1. Save the feed and the notes that explain it.

Keep the coverage note, the date note, and the source receipt together. A source receipt records which checked rows you used. Host the file only when your reader needs access. Hosting means placing the file where the reader can fetch it. This article does not publish a live feed. It does not reveal a private access key. It does not schedule refreshes. It does not send alerts. [Tracking unseen jobs](/en/articles/jobs-fresh-alerts/) is a separate task.

## Read the fictional two-item feed

The feed in this section is original fiction. It teaches the rules. It is not a result from the Actor. It is not proof that only two jobs exist.

The sample has two source listings. Their source IDs are the text values 000101 and 000102. J1 and J2 are only labels for those two fictional rows. The GUIDs are indeed:000101 and indeed:000102. J1 has a confirmed publication time of 12 October 2026 at 09:30 UTC. J2 has an unknown publication time, so its publication date is unavailable.

| Row | Source ID | GUID | Item link | Publication date |
| --- | --- | --- | --- | --- |
| J1 | 000101 | indeed:000101 | https://jobs.example/J1?role=analyst&city=Austin | Mon, 12 Oct 2026 09:30:00 GMT |
| J2 | 000102 | indeed:000102 | https://jobs.example/J2 | Unavailable |

J1 has the title Café Analyst & SQL. Its description is Check values < 10 & explain results. J2 has the title Report Analyst. Its description is Publication time unavailable. Observed 2026-10-13 05:50 UTC. That observation time is not a publication time.

The J1 title tests Unicode and an ampersand. The J1 description tests a less-than sign and an ampersand. The J1 link tests a query string. The feed stores those characters in safe XML text.

| Source text | Text written in the feed |
| --- | --- |
| Café Analyst & SQL | Café Analyst &amp; SQL |
| Check values < 10 & explain results. | Check values &lt; 10 &amp; explain results. |
| https://jobs.example/J1?role=analyst&city=Austin | https://jobs.example/J1?role=analyst&amp;city=Austin |

```xml
<?xml version='1.0' encoding='utf-8'?>
<rss version="2.0">
  <channel>
    <title>Fictional checked analyst sample</title>
    <link>https://jobs.example/sample</link>
    <description>Two checked sample rows; source collection was capped.</description>
    <item>
      <title>Café Analyst &amp; SQL</title>
      <link>https://jobs.example/J1?role=analyst&amp;city=Austin</link>
      <description>Check values &lt; 10 &amp; explain results.</description>
      <guid isPermaLink="false">indeed:000101</guid>
      <pubDate>Mon, 12 Oct 2026 09:30:00 GMT</pubDate>
    </item>
    <item>
      <title>Report Analyst</title>
      <link>https://jobs.example/J2</link>
      <description>Publication time unavailable. Observed 2026-10-13 05:50 UTC.</description>
      <guid isPermaLink="false">indeed:000102</guid>
    </item>
  </channel>
</rss>
```

Read the fictional two-item feed

J1 maps to the first item. Its GUID is indeed:000101, and its pubDate is the confirmed UTC time. J2 maps to the second item. Its GUID is indeed:000102. The date gap is the missing pubDate on J2. The J2 description names only the observation time. Do not fill that gap with the time you built the document.

A local document test parsed this XML. It found one channel and two items. It confirmed the required channel text. It confirmed that the two GUIDs are unique and that isPermaLink is false. After parsing, the text matched the Unicode title, the less-than sign, the ampersands, and the original query link. The test read the J1 publication date. It confirmed that J2 has no pubDate. It changed the J1 title, and the GUID stayed indeed:000101.

These are document checks on this fictional sample. The XML parse shows sound structure. The GUID, link, text, and date checks cover the RSS rules used here. No feed-reader test was done. No WordPress test was done. Parse your fictional feed first. Then do a small compatibility check in your own reader.

## Next action

Collect checked rows with the [Indeed Jobs Scraper](https://apify.com/chronometrica/indeed-jobs-scraper) Actor and this public [Indeed Jobs Scraper Task](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-jobs-scraper). Parse a two-item fictional feed first. Confirm its GUIDs, dates, and source links before you import real rows into a reader.

## Sources

- [RSS 2.0 specification](https://www.rssboard.org/rss-specification)
- [XML syntax](https://www.w3.org/TR/xml/#syntax)
- [Indeed Jobs Scraper Actor](https://apify.com/chronometrica/indeed-jobs-scraper)
- [Indeed Jobs Scraper public Task](https://apify.com/chronometrica/indeed-jobs-scraper/examples/indeed-jobs-scraper)
- [API and RSS job import question](https://www.reddit.com/r/webdev/comments/1ewb7wt/api_vs_rss_feeds_for_pulling_job_listings_for_a/)
- [Indeed scraper question](https://www.reddit.com/r/webscraping/comments/1bkjb1q/looking_for_indeed_scraper_easy_free/)