What are the worst times to post on HN?
What are the worst times to post on HN?
Hacker News (HN) is a great way to get eyes on a project. But we're pretty bad at posting on HN. There's lots of good analysis about best times to post. (seriously - it's an incredibly popular HN topic, probably because there's only so many ways you can slice the free HN dataset without getting into series text analysis - which we could do someday, but not today!).
We've got some data* - can we figure out how to do worse?
The goal of a Show HN post is usually to get visibility and feedback. We can use score as a proxy for that. So we want to optimize for the way that will produce the lowest score.
You could post on a Friday about your project in Swift. You could post it on Christmas Day, when apparently no one has. But we're getting ahead of ourselves - let's look at the data.
Tips
There is no attempt to provide causation, only correlation. There's also no attempts at statistical rigor here. You can do better by importing this dataset (all queries provided as examples) in Trilogy Studio and hack around yourself.
Our Data*
*not really ours, but some handy public sources!
We're going to primarily use the BigQuery Hacker News dataset, which contains all posts since 2006.
A huge shoutout to the BigQuery public data program - it's a fantastic way to make data accessible - both fun and serious. And Bigquery is a very nice engine with a forgiving free tier. (looking at you, snowflake!)
We will pull in BigQuery github data, though this data is stale. If you have a better source, please reach out! (issue on trilogy-public-data would be great).
For purposes here, we'll typically filter to the below. You can view each query for details.
type = 'story'- Titles starting with
Show HN - A "before" window (2023) and an "after" window (2025) so we can see what's shifted
- Excluded deleted/dead items
Where should you host your code?
Tips
Every chart below is generated directly from the BigQuery Hacker News + GitHub public datasets with the Trilogy CLI - each one is a copy into png ... from chart ... statement with some appropriate light/dark mode theming.


- 2023: plain
github.comrepo links scored highest (~22), withgithub.ioandvercel.appbehind. Code got you points. Don't link to code. - 2025:
github.io(GitHub Pages) has jumped to the top (~30) while rawgithub.comlinks cooled to the middle of the pack. - What changed: Code is no longer coolest, but is still too cool Absolutely don't have a custom free page courtesy of GitHub. (don't worry about uptime). A tweet link is a safe way to avoid success.
Host Query
import post;
where
type = 'story'
and is_show_hn
and not deleted and not dead
and create_time.year=2025
select
domain,
avg(score) as avg_domain_score,
count(id) as sample_size
order by
sample_size desc
limit 25;
Best (Worst) Day to Post - Any Day That's Not Fool's Day?


Findings (steady across both years):
- Presidents, Veteran's, Faily. People love them for some reason. Great time to post to get ignored.
- April Fools: Don't joke around, or you're at risk of getting upvotes.
- Friday is generally a consistently good day to be ignroed.
What changed:
- Difficulty decreased: a typical weekday averaged ~17 upvotes in 2023 but only ~9-10 in 2025. Getting ignored has never been easier.
Which Day Query
import post;
import std.date;
where type = 'story' and is_show_hn and create_time.year=2025 and not deleted and not dead
select
CASE
# New Year's Day
WHEN create_time.month = 1 and create_time.day = 1 THEN 'New Years Day'
# Valentine's Day
WHEN create_time.month = 2 and create_time.day = 14 THEN 'Valentines Day'
#Presidents Day (3rd Monday in February)
WHEN create_time.month = 2 and create_time.day_of_week_name = 'Monday'
and create_time.day >= 15 and create_time.day <= 21 THEN 'Presidents Day'
# St. Patrick's Day
WHEN create_time.month = 3 and create_time.day = 17 THEN 'St. Patricks Day'
#April Fools' Day
WHEN create_time.month = 4 and create_time.day = 1 THEN 'April Fools Day'
#Memorial Day (last Monday in May)
WHEN create_time.month = 5 and create_time.day_of_week_name = 'Monday'
and create_time.day >= 25 THEN 'Memorial Day'
# Independence Day
WHEN create_time.month = 7 and create_time.day = 4 THEN 'Independence Day'
# Labor Day (1st Monday in September)
WHEN create_time.month = 9 and create_time.day_of_week_name = 'Monday'
and create_time.day <= 7 THEN 'Labor Day'
# Halloween
WHEN create_time.month = 10 and create_time.day = 31 THEN 'Halloween'
#Veterans Day
WHEN create_time.month = 11 and create_time.day = 11 THEN 'Veterans Day'
#Thanksgiving (4th Thursday in November)
WHEN create_time.month = 11 and create_time.day_of_week_name = 'Thursday'
and create_time.day >= 22 and create_time.day <= 28 THEN 'Thanksgiving'
# Black Friday (day after Thanksgiving)
WHEN create_time.month = 11 and create_time.day_of_week_name = 'Friday'
and create_time.day >= 23 and create_time.day <= 29 THEN 'Black Friday'
#Christmas Eve
WHEN create_time.month = 12 and create_time.day = 24 THEN 'Christmas Eve'
# Christmas
WHEN create_time.month = 12 and create_time.day = 25 THEN 'Christmas'
# New Year's Eve
WHEN create_time.month = 12 and create_time.day = 31 THEN 'New Years Eve'
else create_time.day_of_week_name
end::string::day_of_week_name as fun_label,
id.count,
avg(score) as avg_score,
order by avg_score desc;
Dead Times of Day


Observations:
- Honestly, this is pretty random. Use it as a Rorschach test, take what you will, time your posts for 4/5 AM if you want.
Time of Day Query
import post;
where type = 'story' and is_show_hn and create_time.year=2025 and not deleted and not dead
SELECT
create_time.hour,
id.count,
avg(score) as avg_score;
Post Length: Short Post, No Engagement


That looks like it might go up and to the right - let's call it a positive correlation with length. 0 is the classic link only (and, as the bubble size shows, by far the most common kind of post).
So the sweet spot - 0-100. Keep your post brief enough, and you can avoid those upvotes!
What changed: I think the slope went down in 2025. You still want to be short to be ignored, but it's less risky to go long.
Length Query
import hacker_news;
where type = 'story' and is_show_hn and create_time.year=2025 and not deleted and not dead
select
round( len(text) /100, 0) as size_bucket,
avg(score) as avg_score,
count(id) as sample_size
having sample_size
>5
;
Use These (Unpopular Words)


Top picks:
- 2023: Sales - never a winner, despite bringing in the money. Good advice for your startup too - avoid cashflow like the plague.
- 2025: Crypto and NextJS are dead to people. Now is the time For the crossover.
Keyword Query
import hacker_news;
auto title_word <- unnest(
split(regexp_replace(lower(title), '[(),.:;!?"]', ''), ' ')
);
where type = 'story' and is_show_hn and create_time.year=2025 and not deleted and not dead
select
title_word,
avg(score) as avg_score,
count(id) as sample_size
having sample_size
>20 and regexp_contains(title_word, '[a-z]')
order by avg_score asc
limit 25
;
Avoid (Popular)
The flip side - upvote catnip. Skip these.


Crowd-pleasers:
- 2023:
sqlite,postgres,llama- popular, free, versatile. Key lesson - don't build a brand over time, avoid adding value. - 2025:
years,scratch,spent- proof of time spent. Artisanal, hand-crafted code is far too hot - lean into 'AI Slop' to hit your goals.
Where does "AI" land?
AI isn't one bucket. In 2025, the bare word ai scores ~11 - dead-on the overall average (across a staggering 129k posts): pure table stakes. You have concrete outliers llama (~44), diffusion (~33), tts (~27), openai (~26), claude (~24). But a lot is overdone: agent (~12), agentic (~11), mcp (~10), chatbot (~9). Still best to avoid; risk of ending up with a middling post. (horror!)
Popular Words Query
import hacker_news;
auto title_word <- unnest(
split(regexp_replace(lower(title), '[(),.:;!?"]', ''), ' ')
);
where type = 'story' and is_show_hn and create_time.year=2025 and not deleted and not dead
select
title_word,
avg(score) as avg_score,
count(id) as sample_size
having sample_size
>20 and regexp_contains(title_word, '[a-z]')
order by avg_score desc
limit 25
;
Warning
The BigQuery data used for GitHub is pretty stale, so these last two queries use a longer lookback - all data since 2015 - and are not part of the 2023 → 2025 carousel above. A single-year GitHub split would be mostly noise.
Does Your License Matter?


People love Creative Commons? Stay away from that.
License Query
import github as github;
import post;
property id.github_base_url <- lower(REGEXP_EXTRACT(url, '(https://github\.com/[^/]+/[^/]+)'));
property id.text_github_url <- lower(REGEXP_EXTRACT(text, '(https://github\.com/[^/]+/[^/]+)'));
auto any_github_url <- coalesce(github_base_url, text_github_url);
# not all posts have github URLs
merge github.repo_url into ~any_github_url;
where
type = 'story' and is_show_hn and not deleted and not dead
and create_time.year>2015
select
github.license,
avg(score) as avg_license_score,
count(id) as sample_size
;
Does Your Language Matter?


Whatever you do, don't post something related to Rust if you don't want to hit the front page.
Language Query
import github as github;
import post;
property id.github_base_url <- lower(REGEXP_EXTRACT(url, '(https://github\.com/[^/]+/[^/]+)'));
property id.text_github_url <- lower(REGEXP_EXTRACT(text, '(https://github\.com/[^/]+/[^/]+)'));
auto any_github_url <- coalesce(github_base_url, text_github_url);
merge github.repo_url into ~any_github_url;
where type = 'story' and is_show_hn and not deleted and not dead
and create_time.year>2015
select
github.language,
avg(score) as avg_language_score,
count(id) as sample_size
order by
sample_size desc
limit 25;
If you made it this far
Thanks for going on our journey of diagnosing un-success. We'd like to offer an ignored or money back guarantee, but HN is fickle.
Remember - you get ignored on 100% of the posts you don't make.
: