Skip to main content

KeywordMoves

For free YouTube relative-interest exports, completed-period summaries and independent provider imports, see YouTube demand sources.

KeywordMoves logo

By Kieran Simkin · DanceFlow ecosystem · Vector logo and usage guide.

Source-specific keyword research, local phrase extraction and search-evidence imports. https://kieransimkin.co.uk/

KeywordMoves is the keyword-discovery and search-evidence layer in Kieran Simkin's DanceFlow ecosystem. It provides small, composable Python plugins for generating keywords, finding related language, and attaching clearly labelled evidence such as relative Google Trends interest.

It keeps two plugin systems deliberately separate:

  • keywordmoves.plugins contains keyword sources and analysers.
  • keywordmoves.llms contains interchangeable LLM runtimes.

A keyword plugin that needs generative LLM inference must ask for an LLM plugin by name. It must not import or silently select a model provider of its own.

Before every new workflow

Check the latest stable official release and the actual installed/imported version before starting; do not infer currency from a directory name or a development checkout. Read the current release notes, tagged capability documentation and relevant Potential problems. Use the DanceFlow release preflight and complete capability references.

People and agents should consider the full ecosystem, use all capabilities that materially serve the task, compose supported files/APIs, and improve a demonstrated reusable gap in its canonical tool. Record why no worthwhile change is justified when that is the result. Keep independent tool identities, compatibility, source clocks, privacy, model/download costs and publication/deployment permissions intact. Store weights and model-specific caches in the owning project's ignored models/.

Find your route

Install · Use · Current plugins · Online keyword sources · Monitor keyword evidence over time · Credentials · Write a keyword plugin · Write an LLM plugin · Bundled skill

Install

The core and Google Trends importer have no runtime dependencies:

python -m pip install -e .

Install the local PyTorch/Hugging Face runtime when it is needed:

python -m pip install -e ".[huggingface]"

The first Hugging Face model-backed run downloads the selected model unless llm_local_files_only=true is supplied. Model files stay in the configured Hugging Face cache; the input text is processed locally and is not sent to a hosted API.

The default Qwen checkpoint is pinned to revision 2b01de6d1108f9b2b5e46a726aa678a359b6c03b. KeywordMoves records the selected model and revision in result metadata. A different Hugging Face model remains selectable with --model and --option llm_revision=<commit>.

Install the optional OpenAI runtime separately (no PyTorch required):

python -m pip install -e ".[openai]"

Unlike the local Hugging Face runtime, selecting --llm openai sends the supplied prompts to OpenAI's hosted API. API usage may incur charges.

Install the independent spaCy keyword extractor and its English pipeline:

python -m pip install -e ".[spacy]"
python -m spacy download en_core_web_sm

The pipeline download is an explicit setup step, not something KeywordMoves performs automatically. Subsequent extraction with this pipeline is local.

Install the alternative NLTK extractor and the data used by its default features:

python -m pip install -e ".[nltk]"
python -m nltk.downloader punkt_tab averaged_perceptron_tagger_eng maxent_ne_chunker_tab words stopwords wordnet

This is also local after setup. KeywordMoves never downloads NLTK data implicitly. For offline/custom data directories and smaller installs, see docs/nltk.md.

Install the independent KeyBERT semantic keyword extractor:

python -m pip install -e ".[keybert]"

Its first model-backed run may download the pinned Sentence Transformers model. Text is embedded locally, not sent to a hosted inference API. For prefetching and strictly offline use, see docs/keybert.md.

Event-aligned keyword timing

The offline google-trends plan-event-cycles operation aligns explicitly supplied historical CSVs to verified event dates, preserving each source's query/scope/hash, independent normalization and raw censoring. It separates input quality from musical relevance, abstains on sparse/low-resolution/incomplete cycles, counts distinct non-overlapping events, and reports chronological whole-cycle holdouts when possible. Two cycles stay limited. Optional declared publishing leads/date constraints produce reviewed date-window proposals; no optimal hour, native save, reschedule, volume or cross-platform demand is inferred.

Read the exact contract, quality rules and limits. Run python examples/event_cycles.py --output event-example-output for a real offline demonstration using clearly synthetic indices. Retrospective sampled curves and one successful holdout are not calibrated forecast probabilities.

Event source quality, timing holdouts and separate editorial scheduling

Generated explanatory diagram, not a native interface or a measured campaign result.

Platform-specific hashtag planning

The offline hashtag-planner workbench discovers literal tags in permitted captions, preserves source/platform scope, selects only explicitly reviewed direct-fit choices, and audits final copy against exact asset/plan hashes. For an authorised text-only edit to an existing native post, separate plan-native / audit-native operations bind the exact platform/account/object and captured metadata baseline, without claiming an original media hash or checking media provenance. Both paths reject stale reviews, unverified trends, generic reach bait and copied cross-platform blocks. Editorial ceilings are experiment limits, not claimed optimal counts; Threads/native metadata destinations receive their own treatment. It performs no publishing or paid collection.

Read the contract and platform guidance, then run python examples/hashtag_planner.py --output example-output for a reproducible synthetic demonstration. A passing local audit does not prove native persistence or an audience improvement.

Run python examples/native_metadata_audit.py --output native-example-output for the separate existing-post contract. Keep new-upload audits bound to actual media bytes; native metadata receipts cannot replace them.

Hashtag evidence, review and copy-audit contract, generated diagram

Use

List both plugin layers:

keywordmoves plugins

Import a Google Trends interest-over-time export:

keywordmoves run google-trends `
  --operation import-interest `
  --input .\multiTimeline.csv `
  --option geography=GB `
  --option observed_at=2026-09-30

Extract and expand ideas from a lyrics file with an explicit LLM selection:

keywordmoves run text-library `
  --operation extract `
  --input .\lyrics.txt `
  --llm huggingface-transformers `
  --model Qwen/Qwen2.5-0.5B-Instruct `
  --option limit=20

Use the transparent local extractor without any LLM:

keywordmoves run text-library --operation extract-local --input .\lyrics.txt

Import a reviewed JSON observation file for Search Console, native platform search, autocomplete or a public keyword tool:

keywordmoves run observed-evidence `
  --operation import-observations `
  --input .\keyword-observations.json `
  --format json

The file must use keywordmoves-observations/v1. Each observation requires a phrase, source, actual metric, observation date and platform. Record blocked, empty or unreadable surfaces with availability: unavailable; do not turn an unavailable check into a zero-demand claim. This importer is the supported route for browser-only and authenticated evidence. It deliberately does not scrape sites or call private endpoints.

LLM suggestions are labelled as proposals. They do not establish popularity, search volume, competition, ranking potential, or suitability on a particular platform.

Extract proper nouns and noun chunks with spaCy

keywordmoves run spacy `
  --operation extract `
  --input .\reference.txt `
  --option limit=50

This also extracts named entities, common nouns and contiguous adjective/noun keyphrases. To focus only on the requested noun categories, add:

--option "features=proper-nouns,noun-chunks"

Use --option "text=Your reference text" for inline input, or repeat --input for multiple files/directories. Select another installed spaCy pipeline with --option spacy_model=NAME, not the LLM-specific --model flag.

Results preserve detector labels, entity types, original surface forms, deduplicated occurrence counts and source character offsets. Common-word inflections can be merged without singularising names. Scores describe only heuristic salience in the reference text, not search demand or model confidence. See docs/spacy.md for all options, examples and limitations.

Extract with NLTK

keywordmoves run nltk `
  --operation extract `
  --input .\reference.txt `
  --option limit=50

The nltk and spacy extractors share feature names and candidate metadata. Use --option "features=proper-nouns,noun-chunks" to focus on names and noun phrases, or --option "text=Your reference text" for inline input. NLTK noun chunks use a part-of-speech grammar rather than spaCy's dependency parser, so results need not agree. This initial NLTK implementation is English-only. Names retain their surface forms; ordinary words can be lemmatised with WordNet. See docs/nltk.md for all options, resource setup and limitations.

Rank reference-text phrases with KeyBERT

keywordmoves run keybert `
  --operation extract `
  --input .\reference.txt `
  --option limit=20 `
  --option max_words=3 `
  --option method=mmr `
  --option diversity=0.7

KeyBERT uses embedding similarity, not POS or entity labels. Its default selection is method=cosine; mmr and maxsum select more varied phrases. Use repeated --keyword "phrase" arguments to restrict the vocabulary, or --option "text=Reference text" for inline input. Select another encoder with --option keybert_model=NAME, not the LLM-specific --model switch.

Candidates retain literal source spans; long references are embedded in bounded chunks rather than silently truncated. Scores are not search demand or confidence. See docs/keybert.md for model caching, resource limits, all options and an NLTK/spaCy-to-KeyBERT re-ranking example.

Use OpenAI models

Set the key in the current PowerShell session, then explicitly select OpenAI:

$env:OPENAI_API_KEY = "YOUR_OPENAI_API_KEY"
keywordmoves run text-library `
  --operation extract `
  --input .\lyrics.txt `
  --llm openai `
  --model gpt-4.1-mini `
  --option limit=20

Alternatively, supply the key as a command-line argument:

keywordmoves run text-library --operation extract --input .\lyrics.txt --llm openai --openai-api-key "YOUR_OPENAI_API_KEY"

--openai-api-key overrides --option llm_api_key=... and OPENAI_API_KEY. Prefer the environment variable: command-line keys may appear in shell history or process listings. KeywordMoves does not write the key into result metadata or include raw provider error messages in its normal CLI errors.

The default model is gpt-4.1-mini; use --model to choose another Responses-compatible text model available to your API project. The plugin does not silently switch providers or models. For a reasoning model, a larger token budget may be needed; set --option llm_max_output_tokens=2048 or higher.

See docs/openai.md for Bash and Python examples, supported options, hosted-data behaviour and troubleshooting.

Current plugins

See multi-source research for the source/access inventory, explicit research plans and source-native evidence limits.

Free public-source modules covers reviewed YT Keyword Tool suggestions and Keywords Everywhere's Google planning context, with explicit metric boundaries and a reproducible offline example.

Plugin Kind What it does
vidiq keyword Offline authorised keyword-result imports and the free public overview, with country scope, rounding, censoring and provider dates intact. See imports and comparisons and free-source checks.
ytkeywordtool keyword Reviewed public suggestions and within-batch tier labels; no invented volume, geography or universal score.
candidate-focus keyword Explainable literal phrase filtering and matching terms across saved source results; no combined demand score.
candidate-shortlist keyword Applies an explicit dated editorial review to an exact hashed candidate pool, limits repeated intent groups and reports shortages without padding or changing native metrics.
research-batch keyword Validates and runs explicit bounded multi-source plans, preserving subjects, units and unavailable tasks.
seranking keyword Regional third-party keyword estimates and related/question/long-tail language with a documented credit guard; account access requires separate approval.
wikimedia-pageviews keyword Article-reading attention over declared completed daily windows; no keyword-demand interpretation.
itunes-catalog keyword Apple song-title, credited artist and genre context without popularity metrics or media downloads.
google-search keyword Google Search Console, Trends exports, search observations and page audits with source-specific metrics.
bing-search keyword Bing Webmaster, reviewed search results and keyword planning evidence.
youtube, tiktok, instagram, reddit keyword Separate platform workbenches for authorised exports, native observations, local reference text and supported providers. Read each module's access and metric limits.
google-trends keyword Imports Trends CSVs or automatically downloads native YouTube Search interest and related-query exports through an authorised connected browser; preserves relative indices and growth separately from absolute volume.
keybert keyword Ranks literal reference-text phrases with local sentence embeddings; supports cosine similarity, MMR and Max Sum selection. No generative LLM required.
nltk keyword Extracts proper nouns, grammar-based noun chunks, named entities and keyphrases locally with NLTK. No LLM required.
native-export keyword Imports reviewed platform CSV reports with explicit columns, metric units and missing/censored states.
observed-evidence keyword Validates and imports dated browser, account and public-tool observations without scraping authenticated or private interfaces.
spacy keyword Extracts proper nouns, noun chunks, named entities and useful noun/adjective phrases from reference text with a local spaCy pipeline. No LLM required.
text-library keyword Extracts contiguous Unicode phrases and source spans with extract-literal, retains legacy local associations, or asks an explicitly selected LLM for semantic proposals.
huggingface-transformers LLM Runs a local Hugging Face seq2seq or causal instruction model through PyTorch. Its default is the Apache-2.0 Qwen/Qwen2.5-0.5B-Instruct, pinned to a reviewed model revision.
openai LLM Uses OpenAI-hosted text models through the Responses API. Accepts OPENAI_API_KEY or --openai-api-key; the model is selectable with --model.

Google's official Trends API is currently an access-controlled alpha. The built-in plugin therefore supports reproducible CSV exports now rather than depending on the archived, unofficial pytrends scraper or guessing an API contract. Since 0.5.6, google-trends/collect-browser and the packaged keywordmoves/browser/google_trends.mjs helper operate the native CSV download and pass the result directly to the existing parser. No manual export/import is needed. One plain search term (or up to five matched interest-comparison terms), eight supported countries, three date presets and one chosen interest/related-query chart are checked against the visible UI and CSV. Interest summaries exclude unfinished periods; source request counts remain unknown. An authorised dedicated browser tab/driver is required; the CLI does not start a browser. See automatic collection. A live official-API backend can be added when access and its exact contract are available.

Since 0.5.7, google-trends/assess-interest gives each term its completed-period mean index, reporting bounds for <1 censoring, positive/threshold-zero period counts and a rank only within the same comparison export. Separate searches each normalise independently: their peaks or means cannot rank demand against one another. Missing values stay unavailable; reporting bounds are not statistical confidence intervals, and no absolute monthly volume or cross-provider score is invented. See native demand assessment.

Candidate filtering can require multiple independent context groups in 0.5.7: --option "require_groups=song|songs|music;grief|loss|mourning" keeps literal listening context and a relevant topic together. Each semicolon group must match at least one pipe-separated alternative. Original metrics stay unchanged and every rejected row has a reason; human song-fit review remains necessary. See context filtering.

Since 0.5.8, candidate-shortlist/select turns a reviewed pool into a reproducible shortlist. The caller supplies song fit, listening/navigation intent, wording, intent groups and editorial priorities. Exact input hashes and subject/platform scope prevent stale or misapplied reviews. Defaults select five direct, natural listening targets, at most two from one declared group. Unreviewed or ineligible phrases remain excluded; a shortage stays explicit. Priorities are editorial choices, not demand scores or proof of the globally best keywords. Native source metrics and campaign lifecycle stay unchanged. See shortlist review and the offline example.

Online keyword sources

Free public collection now connects the reviewed sources to their parsers: ytkeywordtool/collect-public queries the public free form without an HTML import, and the packaged connected-browser helper collects Keywords Everywhere Google planning context automatically. A robots-respecting vidiq/collect-public route reports access failures explicitly. See automatic free collection for commands, browser integration, quota bounds and current live limits.

Install the optional networking and HTML parsers:

python -m pip install -e ".[online]"
keywordmoves plugins --kind keyword

The online suite adds twelve documented API adapters: google-ads, search-console, dataforseo, semrush, ahrefs, keywordtool, keywords-everywhere, alsoasked, serpapi, brave-suggest, datamuse and wikipedia. It also includes three explicitly opt-in experimental browser suggestion endpoints, a configurable website-keywords public-HTML extractor, and import-only ubersuggest and answerthepublic adapters.

keywordmoves run datamuse --operation related --keyword "independent music" --option limit=20

$env:SEMRUSH_API_KEY = "YOUR_SEMRUSH_KEY"
keywordmoves run semrush --operation related --keyword "independent music" --option database=uk

Online sources use --keyword seeds, not reference-text inputs or LLM flags. API credentials come from provider-specific environment variables or explicit --option settings. Most commercial APIs require separate API access and may charge credits. A local output limit is not a universal billing cap.

See the online-source guide and research matrix for all eighteen plugins, exact operations, credentials, locale conventions, website query/extraction rules, access limitations and primary-source links. The guide distinguishes API adapters from experimental endpoints and report imports: no authenticated production access is implied by offline tests.

Monitor keyword evidence over time

Use private watchlists and a persistent SQLite history to track source-specific keyword evidence, missing coverage, stale reports and collection failures. Compatible measurements can produce local change alerts; suggestions, rounded estimates and differently normalised indices never become one demand score.

keywordmoves monitor --store ./private/evidence.sqlite init
keywordmoves monitor --store ./private/evidence.sqlite watch --input ./watches.json
keywordmoves monitor --store ./private/evidence.sqlite import --input ./observations.json
keywordmoves monitor --store ./private/evidence.sqlite report --output ./private/coverage.html

Monitoring guide covers histories, provenance, alerts, due plans, opt-in bounded first-party collection, shared account quotas and retention. Native CSV exports covers explicit mappings and the twelve-platform coverage matrix. A public-safe synthetic demo is included in the installed package:

python -m keywordmoves.monitoring.demo --output-dir ./demo-private

Monitoring and import are available without network/NLP extras. Collection composes existing Google Search Console, YouTube Analytics and Bing Webmaster readers; real access still requires an authorised account smoke test. No background job, paid request or account setup starts automatically.

Credentials

Authenticated readers accept explicit CLI options and environment variables. The optional credentials extra adds opt-in Windows Credential Manager, macOS Keychain and Linux Secret Service/KWallet storage, account profiles and presence-only management commands. See credential configuration and authenticated platform access for precedence, scopes and verification limits. OAuth consent and renewal remain external.

Write a keyword plugin

Implement an object with a descriptor and run(request, context) method, then publish it through an entry point:

[project.entry-points."keywordmoves.plugins"]
my-source = "my_package.plugin:MyKeywordPlugin"

The request and result dataclasses are exported from keywordmoves. Use KeywordEvidence to state the source, metric, unit, date, geography and limits instead of flattening unlike evidence into one unexplained score.

Write an LLM plugin

Implement a descriptor and generate(request) -> LLMResult, then register:

[project.entry-points."keywordmoves.llms"]
my-local-runtime = "my_package.llm:MyLocalLLM"

Keyword plugins select it through context.llms.get(name). Model-specific options are passed with the llm_ prefix; for example --option llm_device=cpu becomes the LLM option device=cpu.

Bundled skill

The repository includes skills/research-keyword-database, a reusable workflow for inspecting, expanding, researching, validating and updating the canonical My Songs keyword register. It keeps KeywordMoves candidate generation separate from external demand evidence, requires explicit LLM selection, and preserves the register's evidence and lifecycle semantics.

Current limitations and integration notes

The authenticated-access guide documents the required first-party read scopes, secure Windows credential injection, bounded smoke checks and separate browser, connector and local API access states.

  • Python 3.10–3.13 is supported; monitoring uses standard CPython SQLite support. Optional NLP and provider integrations need their documented extras and source permissions.

  • Source text frequency, semantic relevance, search-result samples, platform observations and provider estimates answer different questions. They are not a universal ranking score.

  • Literal extraction preserves contiguous Unicode phrases and source offsets. Its English boundary stopwords are not a language-aware tokenizer; review other languages explicitly.

  • Browser-observation capture paths and hashes are supplied by the caller. The importer preserves their lineage but does not certify the source capture.

  • Online integrations require the access, permissions and charge limits documented in their module guides. A local result limit is not a billing ceiling.

  • Monitoring databases and reports can contain private account data; keep them outside public source/packages. Quotas cover only clients sharing the same store and account label.

See literal extraction and reviewed browser observations for the new operations and reproducible examples.

Authenticated app setup: client notices and policy boundaries.

Development

python -m pip install -e ".[dev]"
python -m pytest
python -m ruff check .

OpenAI unit/CLI tests also run without the optional SDK. To run the additional real-SDK transport tests (using in-memory HTTP responses, not billable API calls), install .[dev,openai]. CI installs that extra and runs both test sets.

spaCy API tests use controlled annotated documents and need only the spacy extra; a separate optional test uses a real en_core_web_sm pipeline. See spaCy test instructions for both routes. CI also includes a trained-pipeline smoke-test job.

NLTK API tests need the nltk extra but no downloaded resources; a separate pretrained-model smoke test requires its English data. CI installs that data in Linux and Windows jobs and makes a missing-resource skip a failure. See NLTK test instructions.

KeyBERT adapter/API tests use controlled embeddings and do not download models. Install .[dev,keybert] to run both; separate Linux/Windows CI jobs require a real pinned-model smoke test. See KeyBERT tests.

See docs/plugin-opportunities.md for researched next-plugin candidates and access constraints.

Agents and contributors

Agents using KeywordMoves should inspect its available capabilities, use them fully where they fit the task, and look for useful improvements wherever possible. When a required feature is missing, or a reusable reliability, usability, performance or documentation gap is found, improve this tool rather than maintaining a separate workaround. If no worthwhile change is justified, explain why.

Submit every improvement as a pull request to keywordmoves, with relevant tests, documentation and a reproducible example. Follow CONTRIBUTING.md and AGENTS.md for the contribution and validation workflow.

Project website: https://kieransimkin.co.uk/danceflow/.

Metadata

Release files for keywordmoves 0.6.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for keywordmoves 0.6.2
File Size Uploaded Provenance
keywordmoves-0.6.2.tar.gz 620.7 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for keywordmoves 0.6.2
File Interpreter ABI Platform
keywordmoves-0.6.2-py3-none-any.whl Python 3 none any Details

Total release size: 950.2 kB

Release files / keywordmoves-0.6.2.tar.gz

Download URL keywordmoves-0.6.2.tar.gz
Size 620.7 kB
Tags Source
SHA-256 checksum
How to use checksums
3eaa482b57ab3b888a10e0448a726df51af52df73712a2f27fba96dff593d799
BLAKE2b-256 checksum
How to use checksums
2365f4c34ad6cad6c328f042528a3ffcf16fd1314d8ee205536d1534e1ce4a1c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Release files / keywordmoves-0.6.2-py3-none-any.whl

Download URL keywordmoves-0.6.2-py3-none-any.whl
Size 329.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
3e0d7dc79d3db485fe4cb580062c03411bdc4cac14e0713743c49fe3e43081c4
BLAKE2b-256 checksum
How to use checksums
c053e97373a6c6573564aad4b9bac8e3724ef0ab05c1d3f4574c59dc4ca0c336
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

Security for keywordmoves 0.6.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for keywordmoves-0.6.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 24, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page