Publishing API
Approved organisation pages can publish to Vise automatically β from a CMS, an RSS tool or a script. Automation is welcome when it is honest: every API post shows a small βvia APIβ label, and pages never get paid reach.
Interactive reference (OpenAPI): /api/docs
1. Get an API key
- Your page must be approved (Vise admins review new pages).
- Page owners and admins open
/pages/<handle>/manageβ API keys β Create key. The key is shown once; store it like a password. Revoke it there at any time. - Send it in the header
X-Vise-Api-Key: vise_pk_β¦
2. Endpoints
| Method & path | Auth | Purpose |
|---|---|---|
GET /api/v1/pages/{handle} | β | Public page info |
GET /api/v1/pages/{handle}/posts | β | Public posts of the page |
POST /api/v1/pages/{handle}/media | API key | Upload one image (multipart field βfileβ), returns an image id |
POST /api/v1/pages/{handle}/posts | API key | Publish a post (JSON) |
GET /api/v1/pages/{handle}/posts/{id} | β | One post |
PATCH /api/v1/pages/{handle}/posts/{id} | API key | Edit a post; only the fields you send change (PUT and POST to the same URL work too). Shown as βeditedβ. |
DELETE /api/v1/pages/{handle}/posts/{id} | API key | Delete a post (with its comments and images) |
Post fields
body | Text (required unless images are attached). #hashtags become topics. |
title, kind | kind: text | longform (longform needs a title) |
visibility | public (default) | members |
language | e.g. de, en, hu |
media_ids, alt | Up to 4 image ids from /media, plus one description |
link | Canonical article URL, appended to the body |
link_preview | true = show a preview card (title, summary, picture from your page's Open Graph tags) for link. Default false β useful when you don't upload an image yourself. |
content_labels | nudity | erotic | graphic if they apply |
Text is plain text: HTML tags are removed and HTML entities (e.g. &ouml;, &#8230;) are decoded automatically. Line breaks are kept.
Limits & errors
- 200 posts per page per 24 hours; images: JPEG, PNG, WebP or GIF, max. 10 MB, location metadata removed.
401invalid/revoked key or unapproved page Β·422invalid input Β·429daily limit Β·507storage temporarily full
3. Examples
# 1) upload an image (optional)
curl -X POST http://vise.social/api/v1/pages/pesterlloyd/media \
-H "X-Vise-Api-Key: $VISE_KEY" -F "[email protected]"
# β {"id": "img_β¦", "url": "/media/img_β¦/medium", "width": 1080, "height": 720}
# 2) publish
curl -X POST http://vise.social/api/v1/pages/pesterlloyd/posts \
-H "X-Vise-Api-Key: $VISE_KEY" -H "Content-Type: application/json" \
-d '{"kind": "longform", "title": "Schlagzeile", "body": "Teaser β¦ #ungarn",
"language": "de", "link": "https://www.pesterlloyd.net/β¦",
"media_ids": ["img_β¦"], "alt": "Parlament in Budapest"}'
WordPress
A minimal plugin: publish new WordPress posts to Vise (title, excerpt, link, featured image), update them when edited, delete them when trashed. Store the key in wp-config.php as VISE_API_KEY.
<?php
/**
* Plugin Name: Vise Publisher
* Description: Publishes and updates posts on the Vise page via the Vise API.
*/
const VISE_BASE = 'http://vise.social';
const VISE_PAGE = 'pesterlloyd';
function vise_text($html) {
return trim(html_entity_decode(wp_strip_all_tags($html), ENT_QUOTES | ENT_HTML5, 'UTF-8'));
}
function vise_upload_image($attachment_id) {
$path = get_attached_file($attachment_id);
if (!$path) return null;
$ch = curl_init(VISE_BASE . '/api/v1/pages/' . VISE_PAGE . '/media');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-Vise-Api-Key: ' . VISE_API_KEY],
CURLOPT_POSTFIELDS => ['file' => new CURLFile($path)],
]);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
return $res['id'] ?? null;
}
function vise_payload($post) {
$thumb_id = get_post_thumbnail_id($post->ID);
$media_id = $thumb_id ? vise_upload_image($thumb_id) : null;
return [
'kind' => 'longform',
'title' => vise_text(get_the_title($post)),
'body' => vise_text(get_the_excerpt($post)),
'link' => get_permalink($post),
'language' => 'de',
'media_ids' => $media_id ? [$media_id] : [],
'alt' => $thumb_id ? vise_text(get_post_meta($thumb_id, '_wp_attachment_image_alt', true)) : '',
];
}
function vise_request($method, $path, $payload = null) {
return wp_remote_request(VISE_BASE . '/api/v1/pages/' . VISE_PAGE . $path, [
'method' => $method,
'headers' => ['X-Vise-Api-Key' => VISE_API_KEY, 'Content-Type' => 'application/json'],
'body' => $payload ? wp_json_encode($payload) : null,
'timeout' => 20,
]);
}
// Publish new posts; edit already-sent posts; delete on trash.
add_action('transition_post_status', function ($new, $old, $post) {
if ($post->post_type !== 'post') return;
$vise_id = get_post_meta($post->ID, '_vise_post_id', true);
if ($new === 'publish' && !$vise_id) {
$res = vise_request('POST', '/posts', vise_payload($post));
if (!is_wp_error($res) && wp_remote_retrieve_response_code($res) === 201) {
update_post_meta($post->ID, '_vise_post_id', json_decode(wp_remote_retrieve_body($res), true)['id']);
}
} elseif ($new === 'trash' && $vise_id) {
vise_request('DELETE', '/posts/' . $vise_id);
delete_post_meta($post->ID, '_vise_post_id');
}
}, 10, 3);
add_action('post_updated', function ($post_id, $after, $before) {
$vise_id = get_post_meta($post_id, '_vise_post_id', true);
if (!$vise_id || $after->post_status !== 'publish' || $after->post_type !== 'post') return;
vise_request('PATCH', '/posts/' . $vise_id, vise_payload($after));
}, 10, 3);
Questions or need more endpoints (e.g. editing, deleting)? [email protected]