API v1
API documentation
Manage your public profile and the content and advertisements your account is allowed to access. Super-admin keys can also edit site settings and manage site advertising. The API is designed for agents and uses one personal Bearer key instead of OAuth.
Automatic translation is not available through the API. Prepare every localized text before the request and send it through the manual translation fields or routes. Any auto_translate field is rejected with 422.
1. Create and protect your key
A key can be created in the profile only when the account email is verified and the profile status is trusted or verified. It is shown once, has no expiry, and works only on the site that issued it. Revoke it immediately if it may have been exposed.
Authorization: Bearer YOUR_API_KEY
Accept: application/json
2. Verify access and load references
curl -H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" \
"https://mauricetop.com/api/v1/me"
curl -H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" \
"https://mauricetop.com/api/v1/reference-data?lang=en"
Reference data returns configured languages, permitted content types, categories, active advertisement sections, all live locations localized for the requested language, location types, localized tags and their stable codes, advertisement types, price types and currencies, media types, and accessible block groups. The account response also reports profile and settings access plus separate location, block, and tag read, create, update, and delete permissions.
3. Your public profile
GET /api/v1/profile returns the public profile owned by the current key. Administrators can also manage other existing profiles through the routes below. The profile contact email is separate from the account login email returned by /me. Username, account email, password, verification state, roles, and social-login identifiers are not writable through the profile API. Trust status has a separate permission and route.
curl -X PATCH "https://mauricetop.com/api/v1/profile" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"f_name": "Profile owner",
"about": "<p>Public profile description.</p>",
"email": "public@example.com",
"contacts": {
"website": "https://example.com",
"telegram": "profile_owner"
}
}'
PATCH preserves omitted fields and omitted contact keys. Send null for an optional field or one contact to remove it; contacts: null clears the entire contact map. Supported contacts are website, Instagram, Facebook, Telegram, VK, WhatsApp, and LinkedIn. Handles and matching HTTP/HTTPS profile URLs are normalized into safe public links.
Use GET /profile/translations and PUT/DELETE /profile/translations/{lang} for manual translations of first name, last name, and description. A PUT fully replaces one non-source locale, but the overall language set may remain incomplete and the public site keeps its existing source-language fallback. Profile translation never uses the site's automatic translator.
Upload, replace, or remove the avatar through POST/DELETE /profile/avatar. Upload field image accepts GIF, JPEG, PNG, or WebP up to 10 MB and uses the same image processing pipeline as the profile form.
Keys owned by an admin, super-admin, or manager can find existing profiles with GET /api/v1/profiles?username=infobee. Optional account_email is an exact account-login email filter; combined filters must both match. Results are paginated with per_page from 1 to 100 (default 20). Each result exposes the profile ID, trust status and public fields, without account credentials.
Use that profile ID with GET/PATCH /profiles/{id}, GET /profiles/{id}/translations, PUT/DELETE /profiles/{id}/translations/{lang}, and POST/DELETE /profiles/{id}/avatar. These routes accept the same public fields and uploads as the owner routes. Translation languages are checked against the selected profile's source language. Reading requires content:read; every change requires content:write. The current administrative role and key eligibility are checked on every request. Missing profiles return 404; insufficient access returns 403. Avatar replacement or deletion physically removes the previous image and thumbnail; deleting a translation never deletes a profile or account.
admin, super-admin, and manager can change trust status with PATCH /profiles/{id}/status and a JSON body such as {"status":"verified"}. This requires content:write and accepts only guest, known, trusted, verified, or blocked. Managers can read profiles and change status; changing another profile's public fields, translations or avatar requires admin or super-admin. A change away from trusted/verified immediately prevents that account from using API keys.
/me reports profile.read_others, profile.update_others, and profile.update_status for discovering this access.
4. Locations
Locations are one shared hierarchy record with localized name and text. Their numeric ID, slug, parent, type, coordinates, options, URN, public path, and cover are common to every language. Management requires create location, edit location, or delete location in addition to the corresponding content:read or content:write token ability.
curl -X POST "https://mauricetop.com/api/v1/locations" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lang": "en",
"type": "city",
"name": "Example city",
"slug": "example-city",
"text": "<p>A useful local guide.</p>",
"translations": {
"ru": {"name":"Пример города","text":"<p>Полезный местный путеводитель.</p>"}
}
}'
GET /locations supports lang, type, parent_id, slug, updated_after, and pagination up to 100. The language localizes the response and does not hide rows. GET /locations/tree returns the complete nested tree. New locations must include a ready manual name in every site language; when source text exists, every language also needs ready text.
Use PUT /locations/{id}/translations/{lang} to fully replace or repair a non-source name and text. Translations cannot be deleted. PATCH preserves omitted fields and requires coordinated translations when source name or text changes. Slug or parent changes recalculate descendant URNs. Only an unused leaf can be soft-deleted; its stored cover and metadata remain recoverable. Cover replacement and explicit removal physically delete the old file.
5. Localized blocks
Blocks are reusable localized HTML fragments addressed by an internal code. The management routes require an existing create block, edit block, or delete block permission in addition to the content token ability. Because block HTML can change public layouts, scripts, cookie notices, and footer content, these permissions must be treated as administrative access.
curl -X POST "https://mauricetop.com/api/v1/blocks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lang": "en",
"code": "site-notice",
"name": "Site notice",
"group": "homepage",
"text": "<p>Important local notice.</p>",
"active": true,
"translations": {
"ru": {"text":"<p>Важное местное уведомление.</p>"},
"id": {"text":"<p>Pemberitahuan lokal penting.</p>"}
}
}'
Creation must include ready text for every language returned for the current site and is atomic. Lists accept lang, group, code, active, updated_after, and per_page up to 100. A source-text PATCH must include the corresponding ready manual text for every other language. Use PUT /blocks/{id}/translations/{lang} for a complete targeted correction.
Block images use POST/DELETE /blocks/{id}/image and accept GIF, JPEG, PNG, or WebP up to 5 MB. Attachments use POST/DELETE /blocks/{id}/file and accept PDF, Office documents, TXT, or RTF up to 10 MB. Explicit asset deletion removes the file physically. Deleting a block soft-deletes every live language with its code but preserves its stored assets.
6. Site settings
Settings are available only to a super-admin key with content:read or content:write. Use GET /api/v1/settings to list them, optionally filtered by key or group, and GET /api/v1/settings/{id} to read one setting and its saved translations. The list returns IDs and stable keys so clients can locate settings without changing their keys.
curl -X PATCH "https://mauricetop.com/api/v1/settings/123" \\
-H "Authorization: Bearer YOUR_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{"value":"<h1>Vietnam by Anilau</h1><p>Local listings and stories.</p>"}'
curl -X PUT "https://mauricetop.com/api/v1/settings/123/translations/ru" \\
-H "Authorization: Bearer YOUR_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{"value":"<h1>Вьетнам с Anilau</h1><p>Частные объявления и истории о жизни в стране.</p>"}'
PATCH /settings/{id} changes only supplied name, type, group, or source-language value; the key is read-only. Values are sanitized as rich HTML. PUT /settings/{id}/translations/{lang} creates or replaces one complete translated value; the source language is English. Successful writes clear the settings cache. Invalid languages or payloads return 422; non-super-admin keys receive 403.
7. Localized tags
Tags are linked across languages by the stable public code returned as code in JSON and stored independently on each site. Tag management reuses content:read and content:write, plus the account permissions create tag, edit tag, and delete tag. New tag sets must cover every language returned by this site's reference data.
curl -X POST "https://mauricetop.com/api/v1/tags" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lang": "en",
"code": "remote-work",
"name": "Remote work",
"slug": "remote-work",
"text": "<p>Guides for remote workers.</p>",
"active": true,
"translations": {
"ru": {"name":"Удалённая работа","slug":"udalennaya-rabota","text":"<p>Материалы для удалённой работы.</p>"},
"it": {"name":"Lavoro remoto","slug":"lavoro-remoto"},
"uk": {"name":"Віддалена робота","slug":"viddalena-robota"},
"id": {"name":"Kerja jarak jauh","slug":"kerja-jarak-jauh"}
}
}'
Use GET/PATCH/DELETE /tags/{id} for one localized row and its complete code group, and GET /tags/{id}/translations plus PUT/DELETE /tags/{id}/translations/{lang} for ready manual translations. A repeated PUT preserves an omitted slug; a new non-Latin name falls back to code-lang.
POST /tags/{id}/translations/link with tag_id links one isolated legacy tag in another language; conflicts return 409. POST/DELETE /tags/{id}/cover replaces or physically removes the addressed locale's image. Deleting a translation or whole code group is a soft delete and keeps pivots and images for restoration.
8. Advertisement sections
Super-admin keys can list, create, read, patch, add a cover to, and delete advertisement sections through /api/v1/sections. Other accounts continue to receive active sections through reference-data, but cannot use the management routes.
curl -X POST "https://mauricetop.com/api/v1/sections" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"lang": "en",
"name": "Software development",
"slug": "software-development",
"title": "Software development services",
"text": "A complete description for this site.",
"translations": {
"ru": {"name":"Разработка ПО","title":"Услуги разработки ПО","text":"Полное описание раздела."},
"id": {"name":"Pengembangan perangkat lunak","title":"Layanan pengembangan perangkat lunak","text":"Deskripsi lengkap untuk situs ini."}
}
}'
Creation is atomic: every language returned by this site's reference data must be complete before the row is inserted. If a source name, title, teaser, or text changes later, send a ready manual value for the same field in every site language. Changing a slug or parent refreshes descendant section and advertisement URLs. Deletion is allowed only for an unused leaf section.
Each site has its own database and identifiers. A client publishing the same section to Bali, Ceylon, Mauritius, and Vietnam should keep the same slug, resolve parent identifiers independently, and provide site-specific descriptions plus every language supported by each site.
9. Content
Use /api/v1/content to list, read, create, and patch posts, pages, or categories allowed by the account. Lists accept lang, type, updated_after, and per_page up to 100.
curl -X POST "https://mauricetop.com/api/v1/content" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"type": "post",
"lang": "en",
"name": "A practical island guide",
"teaser": "A detailed introduction long enough for article moderation.",
"text": "ARTICLE BODY OF AT LEAST 500 CHARACTERS",
"accept_rules": true,
"tag_codes": ["travel"],
"location_ids": [1]
}'
Ordinary key holders create moderated posts only. They may patch their own pending or rejected submissions; a rejected article returns to pending. Published articles, pages, and categories require the corresponding account permissions. Article and advertisement HTML supports safe semantic tables, including captions, column and row groups, headers, cells, numeric row/column spans, and header scope, plus HTTPS iframe video embeds from the configured provider allow-list. Protocol-relative embed URLs are normalized to HTTPS. Scripts, unapproved frames, event handlers, inline styles, srcdoc, and other unsafe attributes are removed. Use tag_codes to resolve the correct localized tags for the content language. The legacy tags field accepts only exact names of existing tags; unknown names return 422, and the two fields cannot be sent together.
10. Advertisements
A super-admin key with ads:write may create an advertisement for another account by including user_id; check /me.ads.assign_owner. For example, find InfoBee with GET /api/v1/profiles?username=infobee and use the returned user_id, not profile id. Resolve it separately on each site. Keep the editorial API key for creation, edits, translations and media. Omission assigns the authenticated account; other roles cannot supply this field. The selected owner's status determines initial moderation, and blocked owners are rejected. Verify data.user_id after creation. PATCH rejects user_id and cannot transfer an existing advertisement.
Advertisement tags form one set of up to 20 semantic codes. Active variants must exist for the source language and every saved text translation; a missing or inactive variant returns 422 with its code and language. Creation saves the advertisement, translations and tag links atomically. Changing tags, source language or text translations recalculates these links. Omitted PATCH fields preserve tags; an empty list clears them. Responses return source-language tags with lang and a tags_by_language map for publication languages. Price comments and media captions do not add publication languages. Public tags use the current language only; older source-only links resolve by their shared code.
Use /api/v1/ads to manage advertisements. Creation requires an active section, language, type, name, and at least one location. Supported type codes are offer, demand, promo, info (shown as Events), and place (Places and venues). Public contact is optional for place and info and required for other types. Place advertisements never expire and require a physical address; a map link is optional. Structured events use their final event date instead of ordinary expiration. Omitted PATCH fields and relations stay unchanged.
curl -X POST "https://mauricetop.com/api/v1/ads" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-d '{
"section_id": 1,
"lang": "en",
"type": "offer",
"name": "Local airport transfer",
"text": "Private local transfer with direct booking.",
"address": "WhatsApp +00 000 000 000",
"location_ids": [1]
}'
Only the owner may edit an advertisement, except for super-admin access. The create request may also include tag_codes, initial prices, and ready manual translations. For type=place, the allow-listed place object supports a physical address, map URL, optional weekly hours, price level, and amenity codes. For type=info, event requires attendance_mode and, for one-time events, a future start_date, conditionally requires physical venue or online link fields, and supports optional end date, times, organizer, registration, map, and admission fields. Event data is shared by translations and uses the site timezone. Set is_recurring=true for recurring events; start/end dates become optional inclusive period bounds. An optional recurrence accepts frequency=daily, weekly with ISO weekdays (Monday=1), or monthly with month_days (1–31; nonexistent dates are skipped). PATCH replaces the entire schedule, preserves other omitted event fields, and clears the schedule with recurrence=null. Switching back to one-time removes recurrence and requires a start date. needs_confirmation selects the organizer confirmation notice and is effectively true without a recurring schedule. Responses include read-only next_date; after today’s start time, the next occurrence is used. Unscheduled events show no exact date/time and sort last. Recurring Event markup describes the nearest occurrence, never the full period; unscheduled or confirmation-required events use generic Service markup. Online-only confirmed events receive Schema.org Event markup, although Google does not guarantee rich results for them. As with content, tags selects existing exact names only and cannot be combined with tag_codes.
11. Manual translations
List translations with GET /{resource}/{id}/translations. Use PUT /{resource}/{id}/translations/{lang} for a complete language payload. Content, advertisement, and tag APIs can remove a non-source language where their documented route exists; location translations cannot be deleted. Content translations are related Post records and keep the normal moderation and permission rules. Advertisement translations live in the advertisement JSON and may include price comments.
curl -X PUT "https://mauricetop.com/api/v1/ads/123/translations/ru" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Готовый заголовок","title":"Готовый заголовок","teaser":"Готовое краткое описание","text":"Готовый текст объявления"}'
Article/page and advertisement text preserves safe HTTP(S) and mailto: links and automatically links plain HTTP(S), www. and email addresses on API writes, including translations. Existing anchors, attributes and code examples are excluded. Every advertisement body link receives nofollow, including internal and email links; editorial links have no mandatory nofollow. Public advertisement forms keep addresses as text.
Every teaser is plain text: HTML is stripped, line breaks are retained, and URL/email addresses remain inactive text. This also applies to translated teasers, categories, tags and catalogue sections. Input length limits apply before automatically generated link markup.
The API never generates translations and exposes no /translations/auto routes. Clients must prepare every localized name, title, teaser, HTML text, tag choice, and price comment before sending it. The auto_translate field is rejected at any nesting level with 422.
For PUT /content/{id}/translations/{lang}, omitting slug and parent_id preserves those values on an existing translation. A newly created translation whose localized name cannot form a Latin slug uses the predictable fallback source-slug-lang.
12. Prices
Advertisement prices are managed through GET/POST /api/v1/ads/{id}/prices and PATCH/DELETE /api/v1/ads/{id}/prices/{price}. Writable fields are type, value, a currency returned by reference data, discount, validity dates, comment, sort, and translated comments. PATCH preserves omitted fields. Send a JSON number when possible; the API also normalizes common grouped string forms such as 1 500 000 and 1 500 000 before validation.
curl -X POST "https://mauricetop.com/api/v1/ads/123/prices" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"type":"day","value":125,"currency":"usd","comment":"Breakfast included","translations":{"ru":{"comment":"Завтрак включён"}}}'
For every visible price comment in a localized advertisement, prepare translations for the site's other languages. The original comment and translated translations[lang].comment are saved on the price, not inferred from the advertisement text. You can provide them in initial advertisement prices, in a separate price POST or PATCH, or as price_comments keyed by price ID when updating one advertisement language.
Only supported non-source languages are accepted. A price PATCH may contain only translations; it preserves omitted languages and fields. An empty or null translated comment removes that language's translation. Responses retain the original comment and the saved translations map. Public pages display the requested language's comment, falling back to the original when the translation is missing or empty.
13. Covers and media galleries
Upload or replace a cover with a multipart POST to /api/v1/locations/{id}/cover, /api/v1/tags/{id}/cover, /api/v1/sections/{id}/cover, /api/v1/content/{id}/cover, or /api/v1/ads/{id}/cover. Use field image; GIF, JPEG, PNG, and WebP files up to 5 MB are accepted.
Gallery routes are GET/POST /{resource}/{id}/media and PATCH/DELETE /{resource}/{id}/media/{media}. A gallery item is a photo, uploaded file, or external video link with optional name, source, sort, group, and active state. Advertisement galleries are limited to six items. Deleting an item removes its stored file and relation. Content translations use gallery fallback through their shared bind and do not duplicate files.
When publishing or editing a localized article or advertisement, check each named gallery item separately from the parent translation. Send its original name and manually prepared translations for every language returned by that site's reference data. This includes source gallery items inherited by translated articles; update their existing media IDs instead of uploading duplicate files.
For a photo or document upload, send file and bracketed multipart fields such as translations[ru][name]. A video link can use the same media route without a file. To edit existing captions, send JSON to PATCH /api/v1/content/{id}/media/{media} or PATCH /api/v1/ads/{id}/media/{media}:
{"translations":{"ru":{"name":"Подпись"},"en":{"name":"Caption"}}}
Only languages of the current site are accepted, and each caption is at most 255 characters. PATCH changes only supplied languages. An empty or null translated name removes that language's translation. Responses retain name as the original caption and include translations; an untranslated caption displays the original name.
14. Site advertising
Site advertising uses /api/v1/promotions, separately from catalogue advertisements at /api/v1/ads. Only a super-admin can access it, with content:read for reads and content:write for writes. Check me.promotions and the advertising formats, types, eight placement IDs, timezone and video hosts in reference-data.promotions.
POST /api/v1/promotions
{"name":"Summer banner","format":"premium","type":"image"}
PUT /api/v1/promotions/123/translations/en
{"alt":"Summer offer","link_url":"https://example.com/offer"}
POST /api/v1/promotions/123/translations/en/images/desktop
multipart field: image
PUT /api/v1/promotions/123/translations/en
{"published":true}
PATCH /api/v1/promotions/123
{"active":true,"weight":2,"starts_at":"2026-10-04T12:00:00+07:00"}
Formats are premium, square and article_footer; types are image, html and video. New materials default to inactive, weight 1, and independent draft language versions. PATCH metadata and PUT one version preserve omitted fields and every other language. Supply only the intended site languages; there is no mandatory translation into all languages. Image versions require a desktop image and alt text before publication. Optional mobile images use /images/mobile; JPEG, PNG, WebP and GIF up to 8 MB are accepted. DELETE the selected image endpoint to remove that file; unpublish before removing a required desktop image.
HTML accepts advertiser code for an isolated iframe; ordinary links are tracked, and JavaScript can open a saved destination with window.open(AnilauPromo.link(url), '_blank') when the returned value is non-null. The URL must already exist in an ordinary link in desktop or mobile HTML. Video accepts an HTTPS provider embed URL, with autoplay forced off. Dates require whole seconds and an explicit timezone offset or Z, are stored in UTC, and use an inclusive start and exclusive end. Null clears a boundary.
GET /promotions supports name, format, type, active and published language filters. GET /promotions/{id} returns all saved versions without counting a view. DELETE /promotions/{id} archives the material while preserving assets and statistics. Set a version's published to false to unpublish it. GET /promotions/placements returns all seven switches; PATCH /promotions/placements with {"placements":{"home_premium":false}} updates only supplied switches.
Campaigns and period reports
Create a project group with POST /promotions/campaigns and {"name":"FunLab"}. List or read groups with GET, rename with PATCH, archive with DELETE and restore with POST /promotions/campaigns/{campaign}/restore. These routes use the same super-admin role and content abilities. Campaigns belong to one site; archiving preserves membership and statistics and does not stop advertising.
Material writes accept campaign_id: an ID assigns a non-archived group, null clears it, and omission preserves membership. All previous views and clicks follow the material when it moves. Filter materials and reports by campaign=ID or campaign=none.
Add group_by=campaign for project totals over the selected period, or group_by=material to compare creatives. Empty campaigns and zero-event materials remain visible. Aggregated rows contain identifiers, names, archived status, views, clicks and CTR; campaign rows also contain material_count. API defaults to group_by=day for compatibility; the admin defaults to campaigns. CTR always uses total clicks divided by total views.
GET /promotions/statistics?from=2026-10-01&to=2026-10-31 returns daily views, clicks and CTR, filtered by ad, placement and language. Days use the site's timezone. Totals cover the entire filtered period; rows are paginated in groups of 50. CTR is a percentage or null when there are no views. Archived materials remain reportable.
15. Access and errors
401 means the key is missing or invalid; 403 means eligibility, ability, or operation access is missing; 404 hides an inaccessible entity; 409 reports an ambiguous or conflicting block/tag translation group; 422 contains validation errors; 429 is the API rate limit. Successful writes return data; lists also return Laravel pagination links and meta.
Nested prices, translations, and media inherit their parent policy. Never send a real key in chat, source control, logs, query strings, or screenshots. Revoke and recreate a key if it may have been exposed.