You already have the directory. You just have not published it.
Most directory projects die at the same place. Not at the design, not at the templates — at the data. Somebody has to go and get five thousand businesses, clean them, and keep them clean. That is the whole project, and it is why the directory idea usually stays an idea.
If you have run SpiderMaps, SpiderSite, SpiderCompanyData or SpiderPeople, you skipped that step a while ago. The businesses are already sitting in your account.

Every business SpiderIQ has ever seen for you lives in one store, per tenant, merged across all four services. Forty-five fields per record: name, city, state, phone, rating, review count, hours, coordinates, and the rest. Nobody exported it. Nobody has to.
One call is the whole build
Here is the part I want you to actually try, because it takes about a minute.

You create a category — the vertical, "Plumbers" — and you import into it:
directory_create_category(name: "Plumbers", slug: "plumbers",
seo_title_template: "Best {category} in {city} | Acme",
seo_description_template: "Compare {category} in {city}. Ratings, reviews, hours.")
directory_import_from_idap(category_slug: "plumbers",
category_filter: "Plumber", country_code: "US", rating_min: 4.0, limit: 5000)That is the directory. Not the first step of the directory.
What you get back is not a list, it is a site:
/directory/plumbers a hub, listing every city you have plumbers in
/directory/plumbers/miami-florida the plumbers in Miami, best rated first
/directory/plumbers/miami-florida/acme-rooter one business, with hours and contact
/sitemap.xml all of it, already in thereNo page rows. No templates to wire. No deploy — listings are live the moment they land. The two SEO templates you set on the category render server side on every one of those pages, and they generate the title and description of every sitemap entry the category contributes. Which is worth reading twice, because a typo in a placeholder ships to search engines rather than to one page.
You never set the city. It is derived from each listing's city and state as it imports, which is also why you should never make one category per city — one category already spans all of them. That is what the hub is.
The prefix is yours too. /directory is a default, not a contract: rename it to /ls or anything else, per site, and the old addresses keep serving as permanent redirects while the sitemap switches over.
Two shapes, one decision
There are two ways to put those businesses on a page, and picking between them takes ten seconds once you know what separates them.

Import a copy. The rows are copied into the CMS. You get a page per city and a page per business, you can edit any listing by hand, and the copy drifts from your data until you re-import.
Or read it live. Bind a list page to idap.businesses and it reads your corpus at request time, scoped to you, all forty-five fields available to the template. Nothing is copied, so nothing can go stale. The trade is real and I would rather say it plainly than let you find it: in default mode this shape has no working per-item link, so it gives you one page, not a page per business. Write your own links in authored mode, or use the import when you want the deep pages.
Most people want the import. The live binding is the right call for a single browsable index over a corpus that changes daily.
The part that actually decides whether this works
Building the directory is the easy half. Keeping it honest is the half that gets skipped, and it has one specific failure.
An import only ever adds and updates. It never removes. A business that closes, or gets renamed so it derives a different address, keeps its page published. Forever. Every re-import drifts the category a little further from your data, and nothing errors, because from the import's point of view nothing went wrong.

The fix is one flag, and it is off by default:
directory_import_from_idap(category_slug: "plumbers", category_filter: "Plumber",
country_code: "US", rating_min: 4.0, prune: true)prune archives every listing this import did not produce. Archived, not deleted — the row survives and you can bring it back.
And it refuses to run in the two cases where "missing from this import" does not mean "gone": when your filter matched nothing at all, and when the import stopped at its limit. In both, pruning would archive businesses that are perfectly fine. So it declines and tells you which case it hit. That refusal is the answer, not a glitch — running it again unchanged declines again. Change the filter instead.
Nothing here runs on a schedule, deliberately. Re-importing is a decision you make, on a corpus you own.
Where to start
Open Content Studio, click Directory, make one category, and import with a tight filter — a single city, rating four and up. Look at the pages. Then widen it.
The step everyone budgets weeks for is the one you already did.
Full walkthrough: Build a Business Directory. The eleven tools, with their errors: Directory Tools.