Download the PHP package indexnowkit/core without Composer

On this page you can find all versions of the php package indexnowkit/core. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package core

IndexNow client for PHP — indexnowkit/core

Tell Yandex, Bing and the other IndexNow engines which URLs changed, from any PHP application. Batching, debounce, throttling, retry policy, key file handling and the #[IndexNow] rule model, on top of PSR-18 / PSR-17 / PSR-3 / PSR-16 only. The framework adapters (Symfony, Doctrine, Laravel, Yii2, Yii3) and the add-on packages build on it; use it directly in plain PHP, a CMS plugin or a custom framework.

Packagist Downloads CI Conformance Coverage PHPStan PHP

github.com/indexnowkit/php (the php-* repositories are read-only splits)

Who gets notified

Yandex, Bing (and DuckDuckGo via Bing), Naver, Seznam, Yep, Internet Archive, Amazon — every engine in the IndexNow registry. One request to the shared endpoint api.indexnow.org reaches all of them; name engines explicitly (engines: [yandex, bing]) only to reach a single one. Internet Archive has no working direct endpoint at the time of writing — it is reached through api.

Google: no. Google does not support IndexNow, its sitemap ping endpoint is gone and the Indexing API is limited to JobPosting / BroadcastEvent. Keep your sitemap for Google; this library will not pretend otherwise.

Notification, not indexing. IndexNow tells an engine that a URL changed; whether and when the page is crawled and indexed is the engine's decision. See the result in Bing Webmaster Tools (IndexNow Insights) and Yandex.Webmaster (Indexing → Reindex pages); a useful metric is the share of submitted URLs in the index after a few days. Deleted pages: answer 410 (gone for good) or 404 (temporarily); for a move answer 301 and submit both URLs; a soft-404 or a redirect to the home page does harm. Bing's URL Submission API and Google's Indexing API are different protocols and not covered here.

Why this over X

Most IndexNow packages are a thin HTTP client: you collect the URLs, you call it, you read the answer. This family does the part that goes wrong in practice:

Install

If you use a framework, prefer its adapter: it wires everything below through your container and hooks into entity changes. The family:

Package What
indexnowkit/core this package: protocol client, rules, key file, the adapter kit
indexnowkit/doctrine Doctrine ORM listener plus a DBAL middleware, commit-safe
indexnowkit/symfony-bundle Symfony: config, Messenger, key file route, commands, profiler panel
indexnowkit/laravel Laravel: Eloquent observer, queue, key file route, artisan commands
indexnowkit/yii2 Yii2: ActiveRecord events with verify-on-commit, yii2-queue, console controller
indexnowkit/yii3 Yii3: #[IndexNowEvents] on yiisoft/active-record with verify-on-commit, a yiisoft/config plugin, console commands
indexnowkit/sitemap reads a sitemap (index, gzip, text) and submits its URLs; the sitemap command of every adapter
indexnowkit/verify one GET before every submission: noindex, robots.txt, canonical, redirects, origin errors; check --sample
indexnowkit/history what was submitted, when, with what answer: PSR-16 and PDO stores, the history and status commands
indexnowkit/console the check, config, submit, submit-<subject>, explain, key:generate commands (symfony/console classes, their bodies and their definitions); every adapter requires it
indexnowkit/cli no framework: the indexnow binary (Composer, PHAR, Docker image, GitHub Action) — check, submit, sitemap --new-only, key:file, history, status over INDEXNOW_* variables and a state file; cron on any CMS (Bitrix, WordPress, MODX, OpenCart), static sites on deploy
indexnowkit/testing require-dev: the conformance kits (C01–C22, A01–A21), the H01–H06 assertions, the mock IndexNow server

Quick start

submit() never throws for remote problems: every engine × host × batch yields a Result and a log line, and URLs that were not sent (debounced, disabled, dry-run, unknown host) yield a skipped result that says why.

The key file

Search engines verify ownership by fetching https://{host}/{key}.txt, whose body must be exactly the key.

Serve it with 200 OK and text/plain, without redirects; KeyFileResponder::headers() has the right headers. A key file elsewhere on the host is fine with key_location. Check\Checker validates the configuration, fetches every key file and, with liveProbe: true, sends a real probe. 403 always means the key file is wrong; rotation guidance is in docs/operations.md.

What happens to a URL

  1. Normalize — relative paths resolved against base_url, scheme and host lower-cased, IDN hosts to punycode, default ports and fragments removed, dot-segments resolved. Anything that is not a public http(s) URL is dropped with a warning.
  2. De-duplicate within the call, then debounce: URLs sent successfully in the last debounce.per_url seconds are skipped. A failing store never blocks delivery, it just stops de-duplicating and logs a warning.
  3. Group by host and look up the key. Hosts without a key are skipped and never sent under another host's key.
  4. Chunk into at most batch.max_urls URLs, throttle one token per HTTP request, and POST one batch per endpoint: {"host", "key", "keyLocation"?, "urlList"} as application/json; charset=utf-8.
  5. Interpret the answer into a Result and mark successful URLs in the debounce store.

Results

status HTTP reason retryable Meaning
ok 200 — no accepted
pending 202 — no accepted, key verification pending; counts as success
failed 400 invalid_request no malformed request (bug: please report)
failed 403 invalid_key no key file not reachable or does not match
failed 422 unprocessable no URLs do not belong to the host / keyLocation invalid
failed 429 rate_limited yes retryAfter filled when the engine said so
failed 5xx server_error yes
failed — transport yes network failure or timeout
failed — or other unexpected see below a misbehaving HTTP client (retryable) or a status no engine should return (not)
skipped — disabled dry_run debounced no_key invalid_url no nothing was sent

Reason is the stable identifier for metrics and alerts, Result::$error the human sentence; Reason::translationKey() (indexnowkit.reason.<value>) names the message for a UI. Decide whether to retry from Result::$retryable, not from the reason. Result also carries engine, endpoint, host, urls, httpCode and metricLabels(); Result::retryableUrls($results) collects what is worth retrying.

Log lines go to the PSR-3 logger you pass to IndexNowKit::create(). See docs/operations.md for the levels, the exact messages and a "my URL was not submitted" checklist.

Declaring pages: #[IndexNow]

#[IndexNow] is repeatable: one attribute per family of public URLs the object has. Exactly one source per rule — route, resolver, via, url or urls. Class-wide policy goes to #[IndexNowDefaults], whose when is ANDed with each rule's own when (a draft page is never public, whatever the rule says).

Typed parameter sources, next to the plain accessor string (property, getter, is/has method, dotted.path, self):

Other shapes, all real cases:

Rules are inherited from parent classes and identified by name (derived from the source, or given explicitly): a subclass rule whose name repeats an ancestor's replaces it, a new name adds a page.

Deletion semantics

Visibility (when) is evaluated per rule, before and after a change. true → false submits that rule's URLs as a deletion so engines recrawl the 404; false → true is a creation; no transition is an update filtered by fields. Deleting an object whose rule does not apply submits nothing: the page was never public.

when is often a getter (isPublished) while the ORM change set holds the field (published). The convention isPublished → published/is_published and getStatus → status is applied automatically; when the names are unrelated, name the backing fields with whenFields. A status string or enum is not a boolean: use when: new Equals('status', 'published') (IndexNowKit\Attribute\Param\Equals); rules registered at runtime may pass a closure.

Full model, semantics table and the adapter-facing types (UrlRule, RuleSet, RuleRegistry): docs/attribute-reference.md.

urlsFor(), explain() and submitEntity() go through GuardedUrlResolver, which never throws: an invalid attribute is logged and yields no URLs, so a typo cannot break a flush.

Configuration

Option Env Default Meaning
enabled INDEXNOW_ENABLED true false drops every submission (logged at info)
key INDEXNOW_KEY — default key, used for every host not listed in hosts
hosts INDEXNOW_HOSTS (a.com=KEY1,b.com=KEY2) [] per-host {key, key_location, base_url}
strict_hosts INDEXNOW_STRICT_HOSTS false apply the default key only to the base_url host
base_url INDEXNOW_BASE_URL null resolves relative URLs; required outside HTTP requests
engines INDEXNOW_ENGINES ['api'] engine names or custom https:// endpoints
dispatch INDEXNOW_DISPATCH sync adapter-defined delivery mode; the core only reports it
batch.max_urls INDEXNOW_BATCH_MAX_URLS 10000 URLs per request: the protocol's ceiling, not a target
debounce.per_url INDEXNOW_DEBOUNCE_PER_URL 600 seconds before the same URL is sent again (0 = off)
throttle.max_requests_per_minute INDEXNOW_THROTTLE_PER_MINUTE 60 per-process request rate (0 = unlimited)
http.timeout INDEXNOW_HTTP_TIMEOUT 10.0 seconds, applied to clients created by discovery
dry_run INDEXNOW_DRY_RUN false log the request instead of sending it
environment INDEXNOW_ENV / APP_ENV — anything but prod/production without a key turns dry_run on

Also key_file.enabled, http.user_agent and key_location. Every value is validated at construction, so a bad setup fails at boot, not at the first submission. Full reference, per-host overrides, Config::with(), Config::OPTIONS and unknownOptions(): docs/configuration.md.

Retries, queues and bulk

No retries inside a web request: 429/5xx come back as retryable results. Use RetryingSubmitter in CLI, cron and workers, or re-enqueue Result::retryableUrls($results) after (new RetryPolicy())->delayAfter($results, $attempt) seconds. Collect during a unit of work, deliver once:

See docs/retries-and-queues.md for the worker recipe and bulk/migration guidance.

Re-announcing a bulk change from the site's own URL list is the job of the add-on package in the family table (Install); $kit->transport is the transport such consumers read through.

Adapters prove their wiring with Testing\Conformance\CoreConformanceTestCase: extend it, return the facade your container built and its FakeTransport, and the protocol scenarios of the spec run against it.

Testing

IndexNowKit\Testing is part of the published package: FakeTransport (records POSTs, answers queued responses), ArrayLogger, FrozenClock, RecordingDispatcher.

More recipes in docs/testing.md.

Extension points

Interface Default Replace it to
Http\TransportInterface Psr18Transport::discover() use your own HTTP stack (LazyTransport defers building it)
Key\KeyProviderInterface StaticKeyProvider keys from a database, per tenant
Url\UrlNormalizerInterface UrlNormalizer strip tracking parameters, enforce trailing slashes, map hosts
Url\UrlResolverInterface NullUrlResolver — build an AttributeUrlResolver and pass it as resolver: turn objects into URLs your way
Url\RouteUrlResolverInterface — (adapter-provided) bridge your framework's router
Attribute\AttributeReaderInterface AttributeReader RuleRegistry for runtime rules, or your own metadata source
Collector\CollectorInterface Collector a durable outbox, a per-tenant buffer
Debounce\DebounceStoreInterface MemoryDebounceStore Psr16DebounceStore, or your own
Throttle\ThrottleInterface TokenBucket NullThrottle, a shared limiter
Dispatch\DispatcherInterface SyncDispatcher CallableDispatcher for a queue, NullDispatcher
SubmitterInterface Submitter decorate (RetryingSubmitter), record, mock

Pass any of them to IndexNowKit::create() by name, or assemble the graph by hand: Client → Submitter → Collector + DispatcherInterface → IndexNowKit. The pieces a framework adapter wires from its configuration have factories with one source of error texts — Http\TransportFactory::lazy() (http.client), Debounce\DebounceStoreFactory::fromConfig() (debounce.store), Dispatch\DispatcherFactory::fromConfig() (dispatch), fromConfig() on Collector, TokenBucket, AttributeUrlResolver and KeyFileResponder — and Adapter\ConfigFactory turns a raw framework array into a Config without ever throwing from a hook. A container that assembles at runtime describes the whole graph once with Adapter\ServicesBuilder and gets it lazily from Adapter\Services; ORM hooks share Hook\ObserverHelper, queue jobs Retry\WorkerOutcome, commands the runners and Console\Definitions of indexnowkit/console. Writing an adapter? docs/adapters.md.

Exceptions

All exceptions implement IndexNowKit\Exception\IndexNowException: ConfigurationException (invalid Config, attribute or resolver setup), InvalidUrlException (a URL that cannot be submitted, caught by Submitter and dropped with a warning), InvalidArgumentException (programming errors) and Http\Exception\TransportException (network failure, turned into a retryable Result by Client; consumers reading documents through the transport see it; Checker turns it into an error line). Nothing throws out of a lifecycle hook — see the error contract in docs/adapters.md.

Limitations

Requirements

PHP 8.2+, ext-json, ext-filter, a PSR-18 client with PSR-17 factories (symfony/http-client and Guzzle are configured automatically with the timeout and no redirects; other clients are used as is). Optional: ext-intl (IDN via UTS #46, otherwise a pure-PHP punycode encoder).

Versioning

SemVer. Before 1.0, minor versions may contain breaking changes; they are listed in CHANGELOG.md. What is covered by the promise and what is not: docs/bc.md.

Notes for AI assistants

Other packages

PHP the family table under Install
JS/TS @indexnowkit/core, next, prisma (planned)
Python indexnowkit, indexnowkit-django (planned)

Design rationale and the cross-language model: docs/spec. Conformance suite: indexnowkit/spec.

MIT. IndexNow is a trademark of its owner; this project is independent and not affiliated with Microsoft, Yandex or indexnow.org.


All versions of core with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
ext-filter Version *
ext-json Version *
php-http/discovery Version ^1.20
psr/clock Version ^1.0
psr/event-dispatcher Version ^1.0
psr/http-client Version ^1.0
psr/http-factory Version ^1.0
psr/http-message Version ^1.1 || ^2.0
psr/http-server-handler Version ^1.0
psr/http-server-middleware Version ^1.0
psr/log Version ^2.0 || ^3.0
psr/simple-cache Version ^1.0 || ^2.0 || ^3.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package indexnowkit/core contains the following files

Loading the files please wait ...