0
# Content Discovery
1
2
Search and discover content through tags, public feeds, and explore blog social connections like likes and followers.
3
4
## Capabilities
5
6
### Tagged Posts
7
8
Get posts from across Tumblr that are tagged with specific tags.
9
10
```javascript { .api }
11
/**
12
* Get posts tagged with the specified tag
13
* @param tag - The tag on the posts you'd like to retrieve
14
* @param params - Additional filtering and pagination parameters
15
* @returns Promise resolving to tagged posts from across Tumblr
16
*/
17
taggedPosts(tag: string, params?: TaggedPostsParams): Promise<any>;
18
```
19
20
**Usage Examples:**
21
22
```javascript
23
// Get recent posts with a specific tag
24
const catPosts = await client.taggedPosts('cats');
25
console.log(`Found ${catPosts.response.length} cat posts`);
26
27
// Get tagged posts with additional parameters
28
const photoPost = await client.taggedPosts('photography', {
29
before: 1461979830, // Unix timestamp
30
limit: 20,
31
filter: 'text'
32
});
33
34
// Discover trending content
35
const trendingPosts = await client.taggedPosts('tumblr', {
36
limit: 50
37
});
38
39
trendingPosts.response.forEach(post => {
40
console.log(`${post.blog_name}: ${post.summary}`);
41
console.log(`Tags: ${post.tags.join(', ')}`);
42
console.log(`Notes: ${post.note_count}`);
43
});
44
```
45
46
### Blog Likes
47
48
Get the posts that a specific blog has liked (if publicly visible).
49
50
```javascript { .api }
51
/**
52
* Get the likes for a blog
53
* @param blogIdentifier - Blog name or URL
54
* @param params - Pagination parameters for blog likes
55
* @returns Promise resolving to posts liked by the blog
56
*/
57
blogLikes(blogIdentifier: string, params?: BlogLikesParams): Promise<any>;
58
```
59
60
**Usage Examples:**
61
62
```javascript
63
// Get posts liked by a blog
64
const blogLikes = await client.blogLikes('staff');
65
console.log(`Staff blog has liked ${blogLikes.liked_posts.length} posts`);
66
67
// Paginate through blog likes
68
const moreLikes = await client.blogLikes('staff', {
69
limit: 20,
70
offset: 20
71
});
72
73
// Get likes before/after specific time
74
const recentLikes = await client.blogLikes('staff', {
75
after: 1461979830,
76
limit: 10
77
});
78
79
// Discover content through curator blogs
80
const curatorLikes = await client.blogLikes('museumsblog');
81
curatorLikes.liked_posts.forEach(post => {
82
console.log(`Curated post from ${post.blog_name}: ${post.summary}`);
83
});
84
```
85
86
### Blog Followers
87
88
Get the list of blogs following a specific blog (if publicly visible).
89
90
```javascript { .api }
91
/**
92
* Get the followers for a blog
93
* @param blogIdentifier - Blog name or URL
94
* @param params - Pagination parameters for followers list
95
* @returns Promise resolving to blogs following the specified blog
96
*/
97
blogFollowers(blogIdentifier: string, params?: FollowersParams): Promise<any>;
98
```
99
100
**Usage Examples:**
101
102
```javascript
103
// Get followers of a blog
104
const followers = await client.blogFollowers('popular-blog');
105
console.log(`Found ${followers.users.length} followers`);
106
107
followers.users.forEach(follower => {
108
console.log(`${follower.name}: ${follower.url}`);
109
if (follower.updated) {
110
console.log(` Last active: ${new Date(follower.updated * 1000)}`);
111
}
112
});
113
114
// Paginate through followers
115
const moreFollowers = await client.blogFollowers('popular-blog', {
116
limit: 20,
117
offset: 20
118
});
119
120
// Find potential blogs to follow based on who follows interesting blogs
121
const photoFollowers = await client.blogFollowers('photography');
122
const activeFollowers = photoFollowers.users.filter(user => {
123
const weekAgo = Date.now() / 1000 - (7 * 24 * 60 * 60);
124
return user.updated > weekAgo;
125
});
126
127
console.log(`${activeFollowers.length} active followers of photography blog`);
128
```
129
130
## Parameter Types
131
132
### Tagged Posts Parameters
133
134
```javascript { .api }
135
interface TaggedPostsParams {
136
/** Return posts before this timestamp */
137
before?: number;
138
/** Number of posts to return (1-20) */
139
limit?: number;
140
/** Post format filter */
141
filter?: PostFormatFilter;
142
}
143
144
type PostFormatFilter = 'text' | 'raw';
145
```
146
147
### Blog Likes Parameters
148
149
```javascript { .api }
150
interface BlogLikesParams {
151
/** Number of results to return (1-20) */
152
limit?: number;
153
/** Liked post number to start at */
154
offset?: number;
155
/** Return posts liked before this timestamp */
156
before?: number;
157
/** Return posts liked after this timestamp */
158
after?: number;
159
}
160
```
161
162
### Blog Followers Parameters
163
164
```javascript { .api }
165
interface FollowersParams {
166
/** Number of results to return (1-20) */
167
limit?: number;
168
/** Follower number to start at */
169
offset?: number;
170
}
171
```
172
173
## Response Data Examples
174
175
### Tagged Posts Response
176
177
```javascript
178
{
179
response: [
180
{
181
id: "12345",
182
type: "photo",
183
blog_name: "photography-blog",
184
timestamp: 1461979830,
185
date: "2016-04-29 22:23:50 GMT",
186
tags: ["photography", "nature", "landscape"],
187
summary: "Beautiful sunset over the mountains",
188
note_count: 156,
189
photos: [
190
{
191
caption: "",
192
original_size: {
193
url: "https://example.com/photo.jpg",
194
width: 1280,
195
height: 720
196
}
197
}
198
]
199
}
200
// ... more posts
201
]
202
}
203
```
204
205
### Blog Likes Response
206
207
```javascript
208
{
209
liked_posts: [
210
{
211
id: "67890",
212
type: "text",
213
blog_name: "writer-blog",
214
timestamp: 1461979830,
215
liked_timestamp: 1461980000,
216
title: "On Writing",
217
body: "Some thoughts about the creative process...",
218
tags: ["writing", "creativity", "inspiration"],
219
note_count: 89
220
}
221
// ... more liked posts
222
],
223
liked_count: 1234
224
}
225
```
226
227
### Blog Followers Response
228
229
```javascript
230
{
231
users: [
232
{
233
name: "follower-blog",
234
url: "https://follower-blog.tumblr.com/",
235
updated: 1461979830
236
},
237
{
238
name: "another-follower",
239
url: "https://another-follower.tumblr.com/",
240
updated: 1461979700
241
}
242
// ... more followers
243
],
244
total_users: 5678
245
}
246
```
247
248
## Content Discovery Strategies
249
250
### Explore by Tag
251
252
```javascript
253
async function exploreTag(tag, depth = 3) {
254
console.log(`Exploring tag: ${tag}`);
255
256
const posts = await client.taggedPosts(tag, { limit: 20 });
257
258
// Analyze posting patterns
259
const blogFrequency = {};
260
const relatedTags = new Set();
261
262
posts.response.forEach(post => {
263
// Track which blogs post about this tag
264
blogFrequency[post.blog_name] = (blogFrequency[post.blog_name] || 0) + 1;
265
266
// Collect related tags
267
post.tags.forEach(t => {
268
if (t !== tag) relatedTags.add(t);
269
});
270
});
271
272
// Find most active blogs for this tag
273
const topBlogs = Object.entries(blogFrequency)
274
.sort(([,a], [,b]) => b - a)
275
.slice(0, 5);
276
277
console.log('Top blogs posting about', tag, ':', topBlogs);
278
console.log('Related tags:', Array.from(relatedTags).slice(0, 10));
279
280
return {
281
posts: posts.response,
282
topBlogs,
283
relatedTags: Array.from(relatedTags)
284
};
285
}
286
```
287
288
### Find Similar Blogs
289
290
```javascript
291
async function findSimilarBlogs(targetBlog, maxSuggestions = 10) {
292
try {
293
// Get what the target blog likes
294
const likes = await client.blogLikes(targetBlog, { limit: 20 });
295
296
// Find blogs that appear frequently in their likes
297
const likedBlogs = {};
298
likes.liked_posts.forEach(post => {
299
likedBlogs[post.blog_name] = (likedBlogs[post.blog_name] || 0) + 1;
300
});
301
302
// Get followers of the target blog
303
const followers = await client.blogFollowers(targetBlog, { limit: 20 });
304
305
const suggestions = Object.entries(likedBlogs)
306
.sort(([,a], [,b]) => b - a)
307
.slice(0, maxSuggestions)
308
.map(([blogName, count]) => ({
309
name: blogName,
310
reason: `Liked ${count} times by ${targetBlog}`,
311
url: `https://${blogName}.tumblr.com/`
312
}));
313
314
return suggestions;
315
316
} catch (error) {
317
console.error(`Cannot analyze ${targetBlog}:`, error.message);
318
return [];
319
}
320
}
321
322
// Usage
323
const similar = await findSimilarBlogs('photography');
324
similar.forEach(blog => {
325
console.log(`${blog.name}: ${blog.reason}`);
326
});
327
```
328
329
### Discover Trending Content
330
331
```javascript
332
async function findTrendingContent(tags = ['tumblr', 'viral', 'trending']) {
333
const trendingPosts = [];
334
335
for (const tag of tags) {
336
const posts = await client.taggedPosts(tag, { limit: 10 });
337
338
// Filter for posts with high engagement
339
const highEngagement = posts.response.filter(post =>
340
post.note_count > 100
341
);
342
343
trendingPosts.push(...highEngagement);
344
}
345
346
// Sort by engagement and recency
347
trendingPosts.sort((a, b) => {
348
const scoreA = a.note_count * (a.timestamp / 1000000);
349
const scoreB = b.note_count * (b.timestamp / 1000000);
350
return scoreB - scoreA;
351
});
352
353
return trendingPosts.slice(0, 20);
354
}
355
356
// Usage
357
const trending = await findTrendingContent();
358
trending.forEach(post => {
359
console.log(`${post.blog_name}: ${post.summary} (${post.note_count} notes)`);
360
});
361
```
362
363
### Build Content Feed
364
365
```javascript
366
async function buildCustomFeed(interests = []) {
367
const feedPosts = [];
368
369
// Get posts for each interest
370
for (const interest of interests) {
371
try {
372
const posts = await client.taggedPosts(interest, { limit: 5 });
373
374
// Add interest category to posts
375
posts.response.forEach(post => {
376
post.category = interest;
377
});
378
379
feedPosts.push(...posts.response);
380
381
// Add delay to avoid rate limiting
382
await new Promise(resolve => setTimeout(resolve, 500));
383
} catch (error) {
384
console.error(`Failed to get posts for ${interest}:`, error.message);
385
}
386
}
387
388
// Sort by timestamp (most recent first)
389
feedPosts.sort((a, b) => b.timestamp - a.timestamp);
390
391
return feedPosts;
392
}
393
394
// Usage
395
const customFeed = await buildCustomFeed([
396
'photography', 'art', 'technology', 'cats'
397
]);
398
399
customFeed.forEach(post => {
400
console.log(`[${post.category}] ${post.blog_name}: ${post.summary}`);
401
});
402
```
403
404
## Privacy and Access Considerations
405
406
### Public vs Private Content
407
408
```javascript
409
// Tagged posts are always public
410
const publicPosts = await client.taggedPosts('photography');
411
412
// Blog likes may be private
413
try {
414
const likes = await client.blogLikes('private-blog');
415
} catch (error) {
416
if (error.message.includes('404')) {
417
console.log('Blog likes are private or blog does not exist');
418
}
419
}
420
421
// Blog followers may be private
422
try {
423
const followers = await client.blogFollowers('private-blog');
424
} catch (error) {
425
if (error.message.includes('403')) {
426
console.log('Followers list is private');
427
}
428
}
429
```
430
431
### Rate Limiting for Discovery
432
433
```javascript
434
async function respectfulContentDiscovery(tags, delayMs = 1000) {
435
const results = [];
436
437
for (const tag of tags) {
438
try {
439
console.log(`Discovering content for: ${tag}`);
440
const posts = await client.taggedPosts(tag, { limit: 10 });
441
results.push({ tag, posts: posts.response });
442
443
// Wait between requests
444
await new Promise(resolve => setTimeout(resolve, delayMs));
445
} catch (error) {
446
if (error.message.includes('429')) {
447
console.log('Rate limit hit, waiting longer...');
448
await new Promise(resolve => setTimeout(resolve, 30000));
449
} else {
450
console.error(`Error discovering ${tag}:`, error.message);
451
}
452
}
453
}
454
455
return results;
456
}
457
```
458
459
## Authentication Requirements
460
461
### Public Discovery (No Auth Required)
462
- `taggedPosts()` - Public tagged content across Tumblr
463
464
### API Key Recommended
465
- `blogLikes()` - Enhanced rate limits for blog likes
466
- `blogFollowers()` - Enhanced rate limits for followers
467
468
### OAuth Not Required
469
Unlike social interactions, content discovery methods work without user authentication, making them ideal for public content exploration and research.